{"id":30029352,"url":"https://github.com/volumefi/etf-purchaser-vyper","last_synced_at":"2026-02-09T05:03:47.533Z","repository":{"id":303939097,"uuid":"983596290","full_name":"VolumeFi/etf-purchaser-vyper","owner":"VolumeFi","description":null,"archived":false,"fork":false,"pushed_at":"2025-07-10T07:59:17.000Z","size":27,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-10T16:28:51.246Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Vyper","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/VolumeFi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2025-05-14T16:06:42.000Z","updated_at":"2025-07-10T07:59:20.000Z","dependencies_parsed_at":"2025-07-10T16:34:32.494Z","dependency_job_id":"9444b73c-9553-4f1f-bbb1-4ac13c6c1798","html_url":"https://github.com/VolumeFi/etf-purchaser-vyper","commit_stats":null,"previous_names":["volumefi/etf-purchaser-vyper"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/VolumeFi/etf-purchaser-vyper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VolumeFi%2Fetf-purchaser-vyper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VolumeFi%2Fetf-purchaser-vyper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VolumeFi%2Fetf-purchaser-vyper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VolumeFi%2Fetf-purchaser-vyper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VolumeFi","download_url":"https://codeload.github.com/VolumeFi/etf-purchaser-vyper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VolumeFi%2Fetf-purchaser-vyper/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269118381,"owners_count":24362994,"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-08-06T02:00:09.910Z","response_time":99,"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":[],"created_at":"2025-08-06T17:02:59.248Z","updated_at":"2026-02-09T05:03:47.487Z","avatar_url":"https://github.com/VolumeFi.png","language":"Vyper","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ETF Purchaser Contract\n\n## Overview\nThis Vyper smart contract facilitates the purchase and sale of ETFs (Exchange-Traded Funds) on the Ethereum blockchain. It allows users to buy and sell ETFs using various assets, including Ether and ERC-20 tokens, with integration to the Compass cross-chain bridge for ETF token transfers.\n\n## Contract Architecture\n\n### State Variables\n- `ASSET`: Immutable address of the primary asset (e.g., USDT)\n- `ASSET_DECIMALS_NUMERATOR`: Conversion factor for asset decimals\n- `ROUTER02`: Uniswap V3 Router02 address for token swaps\n- `WETH9`: WETH contract address\n- `refund_wallet`: Address to receive refunds and deposits\n- `compass_evm`: Compass bridge contract address\n- `paloma`: Cross-chain identifier (bytes32)\n- `fee`: Fee amount for ETF operations\n- `fee_receiver`: Address to receive fees\n\n### External Dependencies\n- **ERC20**: Standard ERC-20 token interface\n- **SwapRouter02**: Uniswap V3 router for token swaps\n- **Weth**: Wrapped Ether contract\n- **Compass**: Cross-chain bridge contract\n\n## Function Documentation\n\n### Constructor\n```vyper\n@deploy\ndef __init__(_router: address, _initial_asset: address, _refund_wallet: address, _compass_evm: address, _fee: uint256, _fee_receiver: address)\n```\n\n**Purpose**: Initializes the contract with core parameters.\n\n**Parameters**:\n- `_router`: Uniswap V3 Router02 address\n- `_initial_asset`: Primary asset address (e.g., USDT)\n- `_refund_wallet`: Wallet to receive deposits and refunds\n- `_compass_evm`: Compass bridge contract address\n- `_fee`: Fee amount for ETF operations\n- `_fee_receiver`: Address to receive fees\n\n**Security Considerations**:\n- Validates router address and retrieves WETH9 address\n- Calculates asset decimal conversion factor\n- Emits initialization events for transparency\n\n**Usage Example**:\n```python\n# Deploy with USDT as primary asset\npurchaser = project.purchaser.deploy(\n    router=\"0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45\",\n    initial_asset=\"0xdAC17F958D2ee523a2206206994597C13D831ec7\",  # USDT\n    refund_wallet=\"0xCdE7fB746AF9C308F10D1df56caF45ac3048653c\",\n    compass=\"0x71956340a586db3afD10C2645Dbe8d065dD79AC8\",\n    fee=0,\n    fee_address=\"0x7C303D43aDF7055ff3Ef88c525803D3ABBDD2860\"\n)\n```\n\n### Access Control Functions\n\n#### `update_compass(_new_compass: address)`\n**Purpose**: Updates the Compass bridge contract address.\n\n**Access Control**: Only callable by current compass contract\n**Security**: Validates SLC switch is unavailable before update\n\n**Usage Example**:\n```python\n# Only callable by current compass contract\npurchaser.update_compass(new_compass_address, sender=compass_contract)\n```\n\n#### `update_refund_wallet(_new_refund_wallet: address)`\n**Purpose**: Updates the refund wallet address.\n\n**Access Control**: Requires Paloma validation\n**Security**: Validates new address is not zero\n\n**Usage Example**:\n```python\n# Requires Paloma validation\npurchaser.update_refund_wallet(new_wallet, sender=compass_contract)\n```\n\n#### `update_fee(_new_fee: uint256)`\n**Purpose**: Updates the fee amount for ETF operations.\n\n**Access Control**: Requires Paloma validation\n**Security**: Validates fee is non-negative\n\n**Usage Example**:\n```python\n# Requires Paloma validation\npurchaser.update_fee(1000000, sender=compass_contract)  # 0.001 ETH\n```\n\n#### `update_fee_receiver(_new_fee_receiver: address)`\n**Purpose**: Updates the fee receiver address.\n\n**Access Control**: Requires Paloma validation\n**Security**: Validates new address is not zero\n\n**Usage Example**:\n```python\n# Requires Paloma validation\npurchaser.update_fee_receiver(new_receiver, sender=compass_contract)\n```\n\n#### `set_paloma()`\n**Purpose**: Sets the Paloma cross-chain identifier.\n\n**Access Control**: Only callable by compass contract when paloma is not set\n**Security**: Validates message data length and sender\n\n**Usage Example**:\n```python\n# Only callable once by compass contract\npurchaser.set_paloma(sender=compass_contract)\n```\n\n### ETF Creation Functions\n\n#### `create_signle_etf(_token_name, _token_symbol, _token_description, _etf_ticker, _expense_ratio)`\n**Purpose**: Creates a new single ETF with specified parameters.\n\n**Parameters**:\n- `_token_name`: ETF token name (max 64 chars)\n- `_token_symbol`: ETF token symbol (max 32 chars)\n- `_token_description`: ETF description (max 256 chars)\n- `_etf_ticker`: ETF ticker symbol (max 40 chars)\n- `_expense_ratio`: Expense ratio (max 1,000,000 = 100%)\n\n**Security Considerations**:\n- Validates all string parameters are non-empty\n- Validates expense ratio ≤ 1,000,000\n- Requires fee payment if fee \u003e 0\n- Refunds excess ETH sent\n\n**Usage Example**:\n```python\n# Create single ETF with 0.5% expense ratio\npurchaser.create_signle_etf(\n    \"Bitcoin ETF\",\n    \"BTCETF\",\n    \"Bitcoin Exchange Traded Fund\",\n    \"BTCETF\",\n    5000,  # 0.5%\n    value=0,  # No fee required\n    sender=user\n)\n```\n\n#### `register_single_etf(_etf_token_denom, _etf_token_name, _etf_token_symbol, _etf_token_description, _etf_ticker, _expense_ratio)`\n**Purpose**: Registers an existing single ETF token.\n\n**Parameters**:\n- `_etf_token_denom`: ETF token denomination (max 96 chars)\n- `_etf_token_name`: ETF token name (max 64 chars)\n- `_etf_token_symbol`: ETF token symbol (max 32 chars)\n- `_etf_token_description`: ETF description (max 256 chars)\n- `_etf_ticker`: ETF ticker symbol (max 40 chars)\n- `_expense_ratio`: Expense ratio (max 1,000,000 = 100%)\n\n**Security Considerations**:\n- Same validation as create_single_etf\n- Used for registering pre-existing tokens\n\n**Usage Example**:\n```python\n# Register existing single ETF\npurchaser.register_single_etf(\n    \"factory/paloma1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7pv/TICKER.123\",\n    \"Bitcoin ETF\",\n    \"BTCETF\",\n    \"Bitcoin Exchange Traded Fund\",\n    \"BTCETF\",\n    5000,  # 0.5%\n    value=0,\n    sender=user\n)\n```\n\n#### `create_composite_etf(_etf_token_name, _etf_token_symbol, _etf_token_description, _expense_ratio, _token0_denom, _token0_position, _token1_denom, _token1_position)`\n**Purpose**: Creates a new composite ETF with two underlying tokens.\n\n**Parameters**:\n- `_etf_token_name`: ETF token name (max 64 chars)\n- `_etf_token_symbol`: ETF token symbol (max 32 chars)\n- `_etf_token_description`: ETF description (max 256 chars)\n- `_expense_ratio`: Expense ratio (max 1,000,000 = 100%)\n- `_token0_denom`: First token denomination (max 96 chars)\n- `_token0_position`: First token position percentage\n- `_token1_denom`: Second token denomination (max 96 chars)\n- `_token1_position`: Second token position percentage\n\n**Security Considerations**:\n- Validates all string parameters are non-empty\n- Validates expense ratio ≤ 1,000,000\n- Validates token positions \u003e 0\n- Validates token0 ≠ token1\n- Validates positions sum to 100\n- Requires fee payment if fee \u003e 0\n\n**Usage Example**:\n```python\n# Create 60/40 BTC/ETH composite ETF\npurchaser.create_composite_etf(\n    \"Bitcoin Ethereum ETF\",\n    \"BTCETH\",\n    \"60% Bitcoin, 40% Ethereum ETF\",\n    3000,  # 0.3%\n    \"factory/paloma1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7pv/BTC.123\",\n    60,\n    \"factory/paloma1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7pv/ETH.456\",\n    40,\n    value=0,\n    sender=user\n)\n```\n\n#### `register_composite_etf(_etf_token_denom, _etf_token_name, _etf_token_symbol, _etf_token_description, _expense_ratio, _token0_denom, _token0_position, _token1_denom, _token1_position)`\n**Purpose**: Registers an existing composite ETF token.\n\n**Parameters**: Same as create_composite_etf plus `_etf_token_denom`\n\n**Security Considerations**:\n- Same validation as create_composite_etf\n- Used for registering pre-existing composite tokens\n\n**Usage Example**:\n```python\n# Register existing composite ETF\npurchaser.register_composite_etf(\n    \"factory/paloma1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7pv/BTCETH.123\",\n    \"Bitcoin Ethereum ETF\",\n    \"BTCETH\",\n    \"60% Bitcoin, 40% Ethereum ETF\",\n    3000,\n    \"factory/paloma1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7pv/BTC.123\",\n    60,\n    \"factory/paloma1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7pv/ETH.456\",\n    40,\n    value=0,\n    sender=user\n)\n```\n\n### Trading Functions\n\n#### `buy(_etf_token, _etf_amount, _amount_in, _recipient, _path, _min_amount)`\n**Purpose**: Purchases ETF tokens using specified input asset.\n\n**Parameters**:\n- `_etf_token`: ETF token address to purchase\n- `_etf_amount`: Amount of ETF tokens to purchase\n- `_amount_in`: Amount of input asset to spend\n- `_recipient`: Address to receive ETF tokens\n- `_path`: Uniswap swap path (optional)\n- `_min_amount`: Minimum output amount (optional)\n\n**Security Considerations**:\n- Validates all addresses are non-zero\n- Validates amounts \u003e 0\n- Requires Paloma and refund wallet to be set\n- Handles direct asset transfers and swaps via Uniswap\n- Supports ETH/WETH conversions\n- Applies decimal conversion for USD amount calculation\n- Refunds excess ETH\n\n**Usage Example**:\n```python\n# Buy ETF with USDT directly\npurchaser.buy(\n    etf_token=\"0x123...\",\n    etf_amount=1000000,  # 1 ETF token\n    amount_in=50000000,  # 50 USDT\n    recipient=user.address,\n    sender=user\n)\n\n# Buy ETF with ETH via swap\npath = b'\\x00' * 20 + b'\\x01' + b'\\x00' * 20  # WETH -\u003e USDT path\npurchaser.buy(\n    etf_token=\"0x123...\",\n    etf_amount=1000000,\n    amount_in=50000000000000000000,  # 50 ETH\n    recipient=user.address,\n    path=path,\n    min_amount=49000000,  # 49 USDT minimum\n    value=50000000000000000000,  # 50 ETH\n    sender=user\n)\n```\n\n#### `sell(_etf_token, _etf_amount, _estimated_amount, _recipient)`\n**Purpose**: Sells ETF tokens via Compass bridge.\n\n**Parameters**:\n- `_etf_token`: ETF token address to sell\n- `_etf_amount`: Amount of ETF tokens to sell\n- `_estimated_amount`: Estimated output amount\n- `_recipient`: Address to receive proceeds\n\n**Security Considerations**:\n- Validates ETF token address and amount\n- Requires Paloma to be set\n- Transfers tokens to contract, approves Compass, then bridges\n- Uses Compass.send_token_to_paloma for cross-chain transfer\n\n**Usage Example**:\n```python\n# Sell ETF tokens\npurchaser.sell(\n    etf_token=\"0x123...\",\n    etf_amount=1000000,  # 1 ETF token\n    estimated_amount=50000000,  # 50 USDT estimated\n    recipient=user.address,\n    sender=user\n)\n```\n\n### Internal Helper Functions\n\n#### `_paloma_check()`\n**Purpose**: Validates Paloma cross-chain messages.\n\n**Security**: Ensures sender is compass contract and Paloma identifier matches.\n\n#### `_safe_approve(_token, _to, _value)`\n**Purpose**: Safely approves token spending.\n\n**Security**: Uses default_return_value=True for compatibility.\n\n#### `_safe_transfer(_token, _to, _value)`\n**Purpose**: Safely transfers tokens.\n\n**Security**: Only transfers if value \u003e 0, uses default_return_value=True.\n\n#### `_safe_transfer_from(_token, _from, _to, _value)`\n**Purpose**: Safely transfers tokens from another address.\n\n**Security**: Only transfers if value \u003e 0, uses default_return_value=True.\n\n## Events\n\nThe contract emits the following events for transparency and off-chain tracking:\n\n- `UpdateCompass`: Compass address updates\n- `UpdateRefundWallet`: Refund wallet updates\n- `UpdateFee`: Fee amount updates\n- `UpdateFeeReceiver`: Fee receiver updates\n- `SetPaloma`: Paloma identifier setting\n- `Buy`: ETF purchase transactions\n- `Sold`: ETF sale transactions\n- `CreateSingleETF`: Single ETF creation\n- `RegisterSingleETF`: Single ETF registration\n- `CreateCompositeETF`: Composite ETF creation\n- `RegisterCompositeETF`: Composite ETF registration\n\n## Security Considerations\n\n### Access Control\n- Compass contract controls critical parameter updates\n- Paloma validation required for sensitive operations\n- Single-use Paloma setting prevents replay attacks\n\n### Input Validation\n- All string parameters validated for non-empty values\n- Address parameters validated for non-zero values\n- Numeric parameters validated for positive values\n- Expense ratios capped at 1,000,000 (100%)\n\n### Reentrancy Protection\n- `@nonreentrant` decorator on all state-changing functions\n- External calls made after state updates\n\n### Fee Handling\n- Fee validation before operations\n- Automatic refund of excess ETH\n- Fee receiver validation\n\n### Token Operations\n- Safe transfer patterns with default return values\n- Proper approval and transfer sequences\n- Decimal conversion handling\n\n## Testing\n\n### Prerequisites\n- Python 3.8+\n- Ape Framework\n- Foundry (for forking)\n\n### Running Tests\nCurrently, this repository does not contain test files. To add tests:\n\n1. Create a `tests/` directory\n2. Add test files with `test_` prefix\n3. Run tests with: `ape test`\n\n### Example Test Structure\n```python\n# tests/test_purchaser.py\nimport pytest\nfrom ape import accounts, project\n\ndef test_constructor():\n    # Test contract deployment\n    pass\n\ndef test_buy_function():\n    # Test ETF purchase\n    pass\n\ndef test_sell_function():\n    # Test ETF sale\n    pass\n\ndef test_access_control():\n    # Test access restrictions\n    pass\n```\n\n### Deployment Scripts\nThe repository includes deployment scripts for multiple networks:\n- `scripts/deploy_eth.py` - Ethereum mainnet\n- `scripts/deploy_arb.py` - Arbitrum\n- `scripts/deploy_base.py` - Base\n- `scripts/deploy_bsc.py` - BSC\n- `scripts/deploy_gnosis.py` - Gnosis Chain\n- `scripts/deploy_op.py` - Optimism\n- `scripts/deploy_polygon.py` - Polygon\n\nTo deploy:\n```bash\n# Load account\nape accounts load Deployer\n\n# Deploy to specific network\nape run scripts/deploy_eth.py --network ethereum:mainnet\n```\n\n## Network Configuration\n\nThe contract supports deployment on multiple networks with fork testing capabilities:\n- Ethereum (with Alchemy fork)\n- Arbitrum (with Alchemy fork)\n- BSC\n- Gnosis Chain\n- Base\n- Optimism\n- Polygon\n\n## License\nApache 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvolumefi%2Fetf-purchaser-vyper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvolumefi%2Fetf-purchaser-vyper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvolumefi%2Fetf-purchaser-vyper/lists"}