{"id":23392057,"url":"https://github.com/buildonpolygon/usdc-lxly","last_synced_at":"2025-09-04T17:38:50.899Z","repository":{"id":193589667,"uuid":"667915919","full_name":"BuildOnPolygon/usdc-lxly","owner":"BuildOnPolygon","description":null,"archived":false,"fork":false,"pushed_at":"2024-07-25T10:41:59.000Z","size":883,"stargazers_count":0,"open_issues_count":2,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-07-25T12:09:44.810Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BuildOnPolygon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":"audit/Polygon_usdc_Oct23Confidential.pdf","citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-18T15:32:11.000Z","updated_at":"2024-07-25T10:42:02.000Z","dependencies_parsed_at":"2023-09-08T22:59:01.427Z","dependency_job_id":"dcbbf66b-7cdc-469d-9ff4-49a699d6780d","html_url":"https://github.com/BuildOnPolygon/usdc-lxly","commit_stats":null,"previous_names":["omnifient/usdc-lxly","buildonpolygon/usdc-lxly"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BuildOnPolygon%2Fusdc-lxly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BuildOnPolygon%2Fusdc-lxly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BuildOnPolygon%2Fusdc-lxly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BuildOnPolygon%2Fusdc-lxly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BuildOnPolygon","download_url":"https://codeload.github.com/BuildOnPolygon/usdc-lxly/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230793616,"owners_count":18281092,"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","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":[],"created_at":"2024-12-22T04:28:16.174Z","updated_at":"2024-12-22T04:28:16.609Z","avatar_url":"https://github.com/BuildOnPolygon.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"# USDC LXLY\n\nThis repo is for CDK chains, and complements [Circle's Bridge Standard for USDC](https://github.com/BuildOnPolygon/usdc-e), by providing the \"bridge contracts\" (which use the canonical bridge of CDK chains).\n\nWhile this README mentions ZkEVM, that's simply because it was the first \"CDK\" chain where this was deployed.\n\n## USDC LXLY Architecture and User Flows\n\n![Diagram](./usdc-lxly.jpg)\n\n## Contracts\n\n- [**BridgedWrapped USDC**](https://zkevm.polygonscan.com/address/0xA8CE8aee21bC2A48a5EF670afCc9274C7bbbC035) (zkEVM) - existing token for USDC in zkEVM, created by the Polygon ZkEVMBridge using the default TokenWrapped ERC20 contract.\n\n- [**USDC-e**](https://zkevm.polygonscan.com/address/0x37eAA0eF3549a5Bb7D431be78a3D99BD360d19e5) (zkEVM) - \"Native\" USDC in zkEVM. This contract matches the current USDC contract deployed on Ethereum, with all expected features. The contract address is different from the current \"bridge wrapped\" USDC in use today, and has the ability to issue and burn tokens as well as \"blacklist\" addresses. [See USDC-e project](https://github.com/BuildOnPolygon/usdc-e).\n\n- [**L1Escrow**](https://etherscan.io/address/0x70E70e58ed7B1Cec0D8ef7464072ED8A52d755eB) (L1) - This contract receives L1 USDC from users, and triggers the ZkMinterBurner contract on zkEVM (through the Polygon ZkEVM Bridge) to mint USDC-e. It holds all of the L1 backing of USDC-e.\n  It's also triggered by the Bridge to withdraw L1 USDC.\n\n- [**ZkMinterBurner**](https://zkevm.polygonscan.com/address/0xBDa0B27f93B2FD3f076725b89cf02e48609bC189) (zkEVM) - This contract receives USDC-e from users on zkEVM, burns it, and triggers the L1Escrow contract on Ethereum Mainnet (through the Polygon ZkEVM Bridge) to transfer L1 USDC to the user.\n  It's also triggered by the Bridge to mint USDC-e when the Bridge receives a message from the L1Escrow that a user has deposited L1 USDC.\n\n- [**NativeConverter**](https://zkevm.polygonscan.com/address/0xd4F3531Fc95572D9e7b9e9328D9FEaa8e8496054) (zkEVM) - This contract receives BridgeWrappedUSDC on zkEVM and mints back USDC-e. It also has a permissionless publicly callable function called \"migrate\" which withdraws all BridgedWrappedUSDC to L1 through the Bridge. The beneficiary address is the L1Escrow, thus migrating the supply and settling the balance.\n\n## Access Control\n\nOn Ethereum Mainnet the L1Escrow Admin \u0026 Owner are [this Safe wallet](https://app.safe.global/home?safe=eth:0xf694C9e3a34f5Fa48b6f3a0Ff186C1c6c4FcE904).\n\nOn the Polygon zkEVM the ZkMinterBurner and NativeConverter's Admin \u0026 Owner are [this Safe wallet](https://app.safe.global/home?safe=zkevm:0x2be7b3e7b9BFfbB38B85f563f88A34d84Dc99c9f).\n\n- L1Escrow\n  - Pauser/Unpauser\n  - Admin Upgrader (via UUPS proxies)\n  - Change Owner (which controls the ability to pause/unpause)\n  - Change Admin (which controls the ability to upgrade the contracts)\n- ZkMinterBurner\n  - Pauser/Unpauser\n  - Admin Upgrader (via UUPS proxies)\n  - Change Owner (which controls the ability to pause/unpause)\n  - Change Admin (which controls the ability to upgrade the contracts)\n  - Minter of USDC-e (set by the USDC-e deploy script)\n  - Burner of USDC-e (set by the USDC-e deploy script)\n- NativeConverter\n  - Pauser/Unpauser\n  - Admin Upgrader (via UUPS proxies)\n  - Change Owner (which controls the ability to pause/unpause)\n  - Change Admin (which controls the ability to upgrade the contracts)\n  - Minter of USDC-e (set by the USDC-e deploy script)\n  - Burner of USDC-e (set by the USDC-e deploy script)\n    - Note: `burn` is never used by the NativeConverter, only `mint`\n\n## Flows\n\n- User Bridges from L1 to zkEVM\n  - User calls `bridgeToken()` on L1Escrow, L1_USDC transferred to L1Escrow, message sent to PolygonZkEVMBridge targeted to zkEVM’s ZkMinterBurner.\n  - Message claimed and sent to ZkMinterBurner, which calls `mint()` on USDC-e, which mints new supply to the correct address.\n- User Bridges from zkEVM to L1\n  - User calls `bridgeToken()` on ZkMinterBurner which calls `burn()` on USDC-e, burning the supply. Message is sent to PolygonZkEVMBridge targeted to L1Escrow.\n  - Message claimed and sent to L1Escrow, which transfers L1_USDC to the correct address.\n- User converts BridgeWrappedUSDC to USDC-e\n  - User calls `convert()` on NativeConverter, BridgeWrappedUSDC is transferred to NativeConverter. NativeConverter calls `mint()` on USDC-e, which mints new supply to the correct address.\n  - Anyone can call `migrate()` on NativeConverter to have all BridgeWrappedUSDC withdrawn via the PolygonZkEVMBridge moving the L1_USDC held in the PolygonZkEVMBridge to L1Escrow.\n\n## Testing and Deploying\n\nFirst, copy `.env.example` to `.env` and set the appropriate environment variables (annotated with TODOs).\n\n### Testing\n\n1. Start anvil: two instances required, one for L1, and one for L2\n\n```bash\n# 1.1 start L1 (ethereum mainnet) anvil - NOTE: using port 8001 for L1\nanvil --fork-url \u003chttps://eth-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_KEY\u003e --chain-id 1 --port 8001 --fork-block-number 17785773\n\n# 1.2 start L2 (polygon zkevm) anvil - NOTE: using port 8101 for L2\nanvil --fork-url \u003chttps://polygonzkevm-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_KEY\u003e --chain-id 1101 --port 8101 --fork-block-number 3172683\n```\n\n2. Deploy and initialize USDC-e in L2.\n\nNote: you will be using Circle's USDC repo https://github.com/circlefin/stablecoin-evm/blob/master/README.md#deployment for this.\n\n3. Copy the address to where USDC-e was deployed (to be used in the next step)\n\n```bash\nFiatTokenV2_2@0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 # example output\n```\n\n4. Set the USDC-e address into `usdc-lxly/.env`\n\n```bash\ncd usdc-lxly/\nADDRESS_L2_USDC=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512\n```\n\n5. Run the usdc-lxly tests\n\n```bash\ncd usdc-lxly/\nforge test -v\n```\n\n### Deployment to Mainnet Forks\n\nNote:\n\n- using `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` as the admin for USDC-e\n- using `0xa0Ee7A142d267C1f36714E4a8F75612F20a79720` as the admin+owner for L1Escrow, ZkMinterBurner, and NativeConverter contracts\n\nA. Follow steps 1-4 from testing\n\nB. deploy and initialize usdc-lxly\n\n```bash\ncd usdc-lxly/\nforge script scripts/DeployInit.s.sol:DeployInit --broadcast -vvvv\n```\n\n### Deployment to Testnet/Mainnet\n\n1. Deploy and initialize USDC-e in L2.\n\nNote: you will be using Circle's USDC repo https://github.com/circlefin/stablecoin-evm/blob/master/README.md#deployment for this.\n\n2. Copy the address to where USDC-e was deployed (to be used in the next step)\n\n```bash\nFiatTokenV2_2@0x00...000\n```\n\n3. Set the USDC-e address into `usdc-lxly/.env`\n\n```bash\ncd usdc-lxly/\nADDRESS_L2_USDC=0x00...000\n```\n\n4. deploy and initialize usdc-lxly\n\n```bash\ncd usdc-lxly/\nforge script scripts/DeployInit.s.sol:DeployInit --broadcast --multi -vvvv\n```\n\n5. verify contracts\n\nUse the `forge flatten` CLI tool to create a single Solidity file for each of the contracts deployed, and then use the Etherscan/Polygonscan Verification GUI to manually verify each contract.\n\n[Etherscan Verification Tool](https://etherscan.io/verifyContract)\n\n[Polygonscan Verification Tool](https://zkevm.polygonscan.com/verifyContract)\n\n- Use \"Solidity (Single File)\" for the Compiler Type\n- Use v0.8.17+commit.8df45f5f for the Compiler version\n- Use \"MIT\" for Open Source License Type\n- Select \"Yes\" from the Optimization dropdown\n- When it asks for contract source code, copy+paste in the result of `forge flatten \u003cpath_to_solidity_file\u003e`\n- When verifying one of the Proxy contracts which take constructor arguments, use [Hashex](https://abi.hashex.org/) to ABI-encode your constructor arguments. For the `bytes` constructor argument, simply leave the argument empty, do not pass \"\" as an argument, as that will be incorrect and your contract will not verify\n\n6. test contracts\n\n```bash\nexport PK=\nexport TESTER=\nexport L1_RPC=\nexport L2_RPC=https://rpc.public.zkevm-test.net\nexport L1_USDC=0x07865c6e87b9f70255377e024ace6630c1eaa37f\nexport L2_USDC=0x34919B92b0CD1B49D9A42c5eef3c3Bd26Bb2E04A\nexport L2_BWUSDC=0xA40b0dA87577Cd224962e8A3420631E1C4bD9A9f\nexport L1_ESCROW=0x0c404525ca97251EaB96140fff132C8D29B4F6A7\nexport MINTER_BURNER=0x70557De0922A8A207C74BeE79567B16751B9392F\nexport NATIVE_CONVERTER=0x5876A2FBAEbBD1F9530f764069d5CCE652767E61\n\n#################################################\n# DEPOSIT TO L1ESCROW\n#################################################\n\n# approve the l1escrow to spend 1 usdc\ncast send --rpc-url $L1_RPC --private-key $PK $L1_USDC \"approve(address,uint256)\" $L1_ESCROW 1000000\n\n# check allowance\ncast call $L1_USDC \"allowance(address,address)\" $TESTER $L1_ESCROW --rpc-url $L1_RPC\n\n# deposit\ncast send --rpc-url $L1_RPC --private-key $PK $L1_ESCROW \"bridgeToken(address,uint256,bool)\" $TESTER 1000000 true\n\n#################################################\n# WITHDRAW FROM ZKMINTERBURNER\n#################################################\n\n# check usdc-e balance\ncast call $L2_USDC \"balanceOf(address)\" $TESTER --rpc-url $L2_RPC\n\n# check usdc-e total supply\ncast call $L2_USDC \"totalSupply()\" --rpc-url $L2_RPC\n\n# approve spending\ncast send --rpc-url $L2_RPC --private-key $PK $L2_USDC \"approve(address,uint256)\" $MINTER_BURNER 1000000\n\n# withdraw\ncast send --rpc-url $L2_RPC --private-key $PK $MINTER_BURNER \"bridgeToken(address,uint256,bool)\" $TESTER 1000000 true\n\n#################################################\n# CONVERT\n#################################################\n\n# approve the native converter to spend 1 bw usdc\ncast send --rpc-url $L2_RPC --private-key $PK $L2_BWUSDC \"approve(address,uint256)\" $NATIVE_CONVERTER 1000000\n\n# convert 1 bw usdc to 1 usdc-e\ncast send --rpc-url $L2_RPC --private-key $PK $NATIVE_CONVERTER \"convert(address,uint256,bytes)\" $TESTER 1000000 \"\"\n\n#################################################\n# MIGRATE\n#################################################\ncast send --rpc-url $L2_RPC --private-key $PK $NATIVE_CONVERTER \"migrate()\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuildonpolygon%2Fusdc-lxly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbuildonpolygon%2Fusdc-lxly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuildonpolygon%2Fusdc-lxly/lists"}