{"id":30896911,"url":"https://github.com/circlefin/recibo","last_synced_at":"2025-09-08T23:47:49.685Z","repository":{"id":279684178,"uuid":"918328378","full_name":"circlefin/recibo","owner":"circlefin","description":"Recibo: encrypted memos for ERC-20 transactions.","archived":false,"fork":false,"pushed_at":"2025-02-26T20:23:57.000Z","size":53,"stargazers_count":5,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-26T21:26:10.499Z","etag":null,"topics":["blockchain","erc20","memos","research"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/circlefin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-17T17:48:59.000Z","updated_at":"2025-02-26T20:24:01.000Z","dependencies_parsed_at":"2025-02-26T21:36:43.095Z","dependency_job_id":null,"html_url":"https://github.com/circlefin/recibo","commit_stats":null,"previous_names":["circlefin/recibo"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/circlefin/recibo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Frecibo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Frecibo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Frecibo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Frecibo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/circlefin","download_url":"https://codeload.github.com/circlefin/recibo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Frecibo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274231140,"owners_count":25245675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","erc20","memos","research"],"created_at":"2025-09-08T23:47:47.430Z","updated_at":"2025-09-08T23:47:49.665Z","avatar_url":"https://github.com/circlefin.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Recibo\n\n## License\nThis work is licensed under `SPDX-License-Identifier: Apache-2.0`. It has not been audited, comes with\nno guarantees, and is provided as is. Use at your own risk.\n\n## About\nWe introduce Recibo, a model smart contract that lets payers add encrypted memos to transactions. It works with standard ERC-20 tokens and also supports gasless transactions using ERC-2612 and ERC-3009. Payers route transactions through Recibo to record their memos as function calldata. Recibo can be used for invoicing, SWIFT ISO20022 messages, BSA Travel Rule, and other applications. \n\nRecibo has four functions to route transactions to the target token. \n- `transferFromWithMsg`: Transfers tokens from msg.sender to receiver with an attached message. Requires prior approval for Recibo to transfer funds.\n- `permitWithMsg`: Approves token spending using ERC-2612 permit with an attached message.\n- `permitAndTransferFromWithMsg`: Performs ERC-2612 permit and transfer with an attached message.\n- `transferWithAuthorizationWithMsg`: Transfers tokens using ERC-3009 authorization with an attached message.\n\nUsing Recibo adds about 10,000 gas overhead to a standard token transaction. \nEvery 100 bytes of the message uses an additional 560 gas.\n\nThis respository contains a Recibo smart contract and a model GaslessToken, a python client, and a CLI. You can deploy the smart contracts on a local anvil test node and interact with them using the CLI.\n\nThe python client library `client/recibo.py` has helper functions\nto create ERC-2612 permits and ERC-3009 authorizations. We also provide this functionality in the Solidity\nunit tests in `test/mock/GaslessTestBase.t.sol`. These could be of independent interest to other projects.\n\n\n## Build and Run\nYou will need to do the following steps:\n\n1. Install Foundry and Anvil\n2. Install Python and dependencies (in a virtual environment)\n3. Build the smart contracts (only need to do this once)\n4. Start anvil in a separate terminal using the command `anvil`.\n5. Call the CLI to deploy and interact with the smart contract.\n\n### Init Submodules\nYou need to initialize git submodules\n```shell\ngit submodule update --init --recursive\n```\n\n### Install Foundry and Anvil\nYou will need Foundry to compile the smart contracts. You\nwill also need to use Anvil if you want to run a local test node.\n```\ncurl -L https://foundry.paradigm.xyz | bash\nfoundryup\n```\n\n### Install Python and dependencies\nYou will need Python 3 to run the client and CLI: https://www.python.org/downloads/\n\nWe recommend you install requirements into a virtual environment.\nGo to the `client` directory and execute the following commands:\n```\n# Create virtual environment .venv in local dir\ncd client\npython3 -m venv ./.venv\n\n# start virtual environment\nsource ./.venv/bin/activate\n\n# Install packages (this will take a few minutes).\npip3 install -r requirements.txt \n```\n\nTo exit the virtual environment\n```\ndeactivate\n```\n\nTo restart the virtual environment\n```\nsource ./.venv/bin/activate\n```\n\n\n### Build \u0026 Test\nTo build the smart contract, go to the Recibo root directory and run:\n```\nforge build\n```\n\nTo run the Foundry unit tests, run:\n```\nforge test\n```\n\nTo run the client unit tests, start the virtual environment and run:\n```\ncd client\npython3 test.py\n```\n\n\n### Run CLI locally\nOpen two terminals. In the first terminal, start Anvil. The Anvil CLI will print\nthe addresses and private keys of ten test accounts. Anvil will fund each account with\n10,000 ETH, so you have more than enough gas to execute transactions. NEVER use these same accounts on mainnet or testnet because these are static well known private keys.\n```\n# Terminal 1: start anvil in foreground\nanvil\n```\n\nIn a second terminal, you will use the Recibo CLI.\n```\n# Terminal 2: build smart contracts (only need to do once)\nforge build\n\n# Go to client directory and start python virtual environment that you previously created.\ncd client\nsource ./.venv/bin/activate\n\n# Deploy GaslessToken and Recibo using a default anvil account private key.\n# account[0] private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# We will call this account Alice in the following commands.\n# Alice will deploy the GaslessToken and Recibo smart contracts. She will also mint 2000 tokens to herself.\npython3 recibocli.py deploy \\\n    --deployer_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \n\n```\n\nBob will need PGP encryption keys to receive encrypted messages.\n```\n# We will generate a 3072-bit PGP key with a passphrase.\n# The public key will be stored in test-data/bob_pub.asc and the private key will be stored in test-data/bob_key.asc.\npython3 recibocli.py gen_encrypt_key \\\n    --outfile test-data/bob \\\n    --keylength 3072 \\\n    --password 'my secret passphrase' \n```\n\nAlice can now send tokens to Bob with an encrypted message. The CLI command\n`transfer_from_with_msg` will first use the owner's private key  to call the approve() function on the \nGaslessToken to give the Recibo contract an allowance, and then\ncall the `transferFromWithMsg` function on the Recibo contract to transfer the funds.\n```\n# account[0] (Alice) private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# account[1] (Bob) address is 0x70997970C51812dc3A010C7d01b50e0d17dc79C8\n# Bob's public key is stored in test-data/bob_pub.asc\npython3 recibocli.py transfer_from_with_msg \\\n    --owner_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\\n    --receiver_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --value 15 \\\n    --message 'Hola my friend! I am sending you 15 USDC for your birthday' \n    --encrypt_pub_keyfile test-data/bob_pub.asc \n```\n\nBob can download and decrypt all messages sent to his address The client will check the Recibo event logs for\nall ApproveWithMsg and TransferWithMsg events and look for the ones where the `MessageTo` field has the\n`receiver_address`. The client will then download the corresponding transactions and attempt to decrypt all the \nmessages using Bob's private key file.\n```\n# account[1] (Bob) address is 0x70997970C51812dc3A010C7d01b50e0d17dc79C8\n# Bob's private key is stored in test-data/bob_key.asc\npython3 recibocli.py read_msg \\\n    --receiver_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --decrypt_keyfile test-data/bob_key.asc \\\n    --password 'my secret passphrase'\n```\n\n#### More CLI Commands\nCommand `permit_with_msg` gives the spender an allowance to spend funds on behalf of the owner. This command only \nworks with tokens that support EIP-2612. The client uses the\nowner's private key to sign an EIP-2612 permit and then executes the `permitWithMsg()` function\non the Recibo smart contract. The Recibo smart contract will call `permit` on the token.\n```\n# account[0] (Alice) private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# account[1] (Bob) address is 0x70997970C51812dc3A010C7d01b50e0d17dc79C8\n# Bob's public key is stored in test-data/bob_pub.asc\npython3 recibocli.py permit_with_msg \\\n    --owner_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\\n    --spender_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --value 20 \\\n    --message 'Bonjour my friend! I permit you to spend 20 USDC on your birthday' \\\n    --encrypt_pub_keyfile test-data/bob_pub.asc \n```\n\nCommand `permit_and_transfer_with_msg` transfers funds from the owner to the receiver. This command only works with tokens that support EIP-2612.\nThe client uses the owner's private key to sign an EIP-2612 permit giving the Recibo smart\ncontract an allowance to spend the owner's funds. Then the client will call\n`permitAndTransferFromWithMsg()` on the Recibo smart contract, which will call `permit()` and then\n`transferFrom()` on the token.\n```\n# account[0] (Alice) private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# account[1] (Bob) address is 0x70997970C51812dc3A010C7d01b50e0d17dc79C8\n# Bob's public key is stored in test-data/bob_pub.asc\npython3 recibocli.py permit_and_transfer_with_msg \\\n    --owner_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\\n    --receiver_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --value 25 \\\n    --message 'Privet my friend! I am sending you 25 USDC for your birthday' \\\n    --encrypt_pub_keyfile test-data/bob_pub.asc \n```\n\nCommand `transfer_with_authorization_with_msg` transfers funds from the owner to the receiver. \nThis command only works with tokens that support EIP-3009.\nThe client uses the owner's private key to sign an EIP-3009 authorization giving the Recibo smart\ncontract an permission to spend the owner's funds. The client will call\n`transferWithAuthorizationWithMsg()` on the Recibo smart contract, which will call `transferWithAuthorization()` \non the token.\n```\n# account[0] (Alice) private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# account[1] (Bob) address is 0x70997970C51812dc3A010C7d01b50e0d17dc79C8\n# Bob's public key is stored in test-data/bob_pub.asc\n# Alice's public key is stored in test-data/alice_pub.asc\npython3 recibocli.py transfer_with_authorization_with_msg \\\n    --owner_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\\n    --receiver_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --value 25 \\\n    --message 'Privet my friend! I am sending you 25 USDC for your birthday' \\\n    --encrypt_pub_keyfile test-data/bob_pub.asc \n```\n\nCommand `deploy_recibo` will deploy just the Recibo contract and point it to an existing ERC-20 token.\n```\n# account[0] (Alice) private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# token_address must point to a deployed ERC-20 token.\npython3 recibocli.py deploy_recibo \\\n    --deployer_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\\n    --token_address 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955\n```\n\n#### Sending Messages and Responses\nYou can insert a response public key into the metadata of your message. Add the optional parameter\n`--response_pub_keyfile \u003cfilename\u003e` to any command. \n\nAlice generates a key pair for herself.\n```\n# The public key will be stored in test-data/alice_pub.asc and the private key will be stored in test-data/alice_key.asc.\npython3 recibocli.py gen_encrypt_key \\\n    --outfile test-data/alice \\\n    --keylength 3072 \\\n    --password 'my secret passphrase'\n```\n\nNow Alice transfers tokens to Bob and includes her public key file in the metadata\n```\n# account[0] (Alice) private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# account[1] (Bob) address is 0x70997970C51812dc3A010C7d01b50e0d17dc79C8\n# Bob's public key is stored in test-data/bob_pub.asc\n# Alice's public key is stored in test-data/alice_pub.asc\npython3 recibocli.py transfer_with_authorization_with_msg \\\n    --owner_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\\n    --receiver_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --value 25 \\\n    --message 'Privet my friend! I am sending you 25 USDC for your birthday' \\\n    --encrypt_pub_keyfile test-data/bob_pub.asc \\\n    --response_pub_keyfile test-data/alice_pub.asc\n```\n\nBob can use `read_msg` to download all of his transactions\n```\n# account[1] (Bob) address is 0x70997970C51812dc3A010C7d01b50e0d17dc79C8\n# Bob's private key is stored in test-data/bob_key.asc\npython3 recibocli.py read_msg \\\n    --receiver_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --decrypt_keyfile test-data/bob_key.asc \\\n    --password 'my secret passphrase'\n```\n\nNow Bob chooses a tx_hash and responds using `respond_to_tx`.\n```\n# account[1] (Bob) private key is 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d\n# You need to get the tx_hash of the transaction to which Bob is responding\n# Bob's public key is stored in test-data/bob_pub.asc\npython3 recibocli.py respond_to_tx \\\n    --owner_private_key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d \\\n    --tx_hash 0441bc03fe5c3beeb218ee23fa65e953cea3ed6a8e76bb8f2111d09ac88b517d \\\n    --message 'Thank you for your generous birthday gift!' \\\n    --response_pub_keyfile test-data/bob_pub.asc\n```\n\nNow Alice can read Bob's response.\n```\n# account[0] (Alice) address is 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\n# Alice's private key is stored in test-data/alice_key.asc\npython3 recibocli.py read_msg \\\n    --receiver_address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \\\n    --decrypt_keyfile test-data/alice_key.asc \\\n    --password 'my secret passphrase'\n```\n\nCommand `send_msg` will send a message using the Recibo contract without transfering any tokens.\n```\n# account[0] (Alice) private key is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80\n# token_address must point to a deployed ERC-20 token.\npython3 recibocli.py send_msg \\\n    --owner_private_key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\\n    --receiver_address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \\\n    --message 'Privet my friend! I am sending you 25 USDC for your birthday' \\\n    --encrypt_pub_keyfile test-data/bob_pub.asc\n```\n\n\n## Dev Notes\nBy default, all commands will use `anvil_config.yaml` as the configuration file. \nTo use your own configuration file, add `--config_file your_filename` as a command argument.\n\n### Deploying to mainnet/testnet\nThe python client uses the file `anvil_config.yaml`.  To connect to mainnet/testnet,\nyou need to update the `rpc_url`. The default setting is to use a local anvil node.\n\n### Smart contract address\nThe client stores the address of the deployed smart contract in local environment files\n- `.gasless_token_env` holds the address of the ERC-20 token\n- `.recibo_env` has the address of the Recibo smart contract.\n\nThen client creates these files automatically when it deploys the smart contracts using\nthe `Recibo.deploy()` function. If you want to use an existing deployed smart contract, you\nneed to modify (or create) these environment files.\n\nSample environment file:\n```\ncontract_address: '0xbFD3511180A40503D807c9249943431Cf847E5b7'\n```\n\n### Deploy new Recibo contract to use an existing ERC-20 token\nYou need to perform the following two steps:\n\n1. Make sure the `.gasless_token_env` file has the address of the ERC-20 token. \n2. Deploy the Recibo contract using the `deploy_recibo` command. Specify the address of the ERC-20 token.\n\nTroubleshooting: check your yaml config file for the name of the token `local_env_file`..\n\n### Configure the CLI to use an existing Recibo contract and ERC-20 token\nYou need to perform the following two steps:\n\n1. Make sure the `.gasless_token_env` file has the address of the ERC-20 token. \n2. Make sure the `.recibo_env` file has the address of the Recibo contract. \n\nTroubleshooting: check your yaml config file for the name of the tokena and Recibo `local_env_file`. These should be two separate files.\n\n### Encryption/Decryption\nThe Recibo smart contract accepts arbitrary encoded messages along with string metadata that\ncan contain encoding information. The CLI uses PGP encryption by default but you can use no encryption algorithms by\nspecifying the `--encrypt_alg_id none` parameter.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcirclefin%2Frecibo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcirclefin%2Frecibo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcirclefin%2Frecibo/lists"}