diff --git a/libs/smart-contracts/.eslintrc.json b/libs/smart-contracts/.eslintrc.json new file mode 100644 index 000000000..9d9c0db55 --- /dev/null +++ b/libs/smart-contracts/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/smart-contracts/README.md b/libs/smart-contracts/README.md new file mode 100644 index 000000000..ba7066bf2 --- /dev/null +++ b/libs/smart-contracts/README.md @@ -0,0 +1,11 @@ +# smart-contracts + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build smart-contracts` to build the library. + +## Running unit tests + +Run `nx test smart-contracts` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/smart-contracts/jest.config.js b/libs/smart-contracts/jest.config.js new file mode 100644 index 000000000..622eb86c7 --- /dev/null +++ b/libs/smart-contracts/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + displayName: 'smart-contracts', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + transform: { + '^.+\\.[tj]s$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/libs/smart-contracts', +}; diff --git a/libs/smart-contracts/package.json b/libs/smart-contracts/package.json new file mode 100644 index 000000000..7532b2f9a --- /dev/null +++ b/libs/smart-contracts/package.json @@ -0,0 +1,5 @@ +{ + "name": "@vegaprotocol/smart-contracts", + "version": "0.0.1", + "type": "commonjs" +} diff --git a/libs/smart-contracts/project.json b/libs/smart-contracts/project.json new file mode 100644 index 000000000..524f4e94a --- /dev/null +++ b/libs/smart-contracts/project.json @@ -0,0 +1,32 @@ +{ + "root": "libs/smart-contracts", + "sourceRoot": "libs/smart-contracts/src", + "targets": { + "build": { + "executor": "@nrwl/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/smart-contracts", + "main": "libs/smart-contracts/src/index.ts", + "tsConfig": "libs/smart-contracts/tsconfig.lib.json", + "assets": ["libs/smart-contracts/*.md"] + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/smart-contracts/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/smart-contracts"], + "options": { + "jestConfig": "libs/smart-contracts/jest.config.js", + "passWithNoTests": true + } + } + }, + "tags": [] +} diff --git a/libs/smart-contracts/src/abis/claim_abi.json b/libs/smart-contracts/src/abis/claim_abi.json new file mode 100644 index 000000000..a2661b202 --- /dev/null +++ b/libs/smart-contracts/src/abis/claim_abi.json @@ -0,0 +1,292 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vesting_address", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes2", + "name": "", + "type": "bytes2" + } + ], + "name": "allowed_countries", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "commitments", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "controller", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "issuers", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "commit_untargeted", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct Signature", + "name": "sig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "tranche", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "expiry", + "type": "uint32" + } + ], + "internalType": "struct Claim", + "name": "clm", + "type": "tuple" + }, + { + "internalType": "bytes2", + "name": "country", + "type": "bytes2" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "claim_targeted", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct Signature", + "name": "sig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "tranche", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "expiry", + "type": "uint32" + } + ], + "internalType": "struct Claim", + "name": "clm", + "type": "tuple" + }, + { + "internalType": "bytes2", + "name": "country", + "type": "bytes2" + } + ], + "name": "claim_untargeted", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes2[]", + "name": "countries", + "type": "bytes2[]" + } + ], + "name": "allow_countries", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes2[]", + "name": "countries", + "type": "bytes2[]" + } + ], + "name": "block_countries", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "permit_issuer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "issuer", + "type": "address" + } + ], + "name": "revoke_issuer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_controller", + "type": "address" + } + ], + "name": "swap_controller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "destroy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/libs/smart-contracts/src/abis/erc20_abi.json b/libs/smart-contracts/src/abis/erc20_abi.json new file mode 100644 index 000000000..03f5c8796 --- /dev/null +++ b/libs/smart-contracts/src/abis/erc20_abi.json @@ -0,0 +1,198 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/libs/smart-contracts/src/abis/erc20_abi_faucet.json b/libs/smart-contracts/src/abis/erc20_abi_faucet.json new file mode 100644 index 000000000..083d2d0e2 --- /dev/null +++ b/libs/smart-contracts/src/abis/erc20_abi_faucet.json @@ -0,0 +1,288 @@ +[ + { + "inputs": [ + { "internalType": "string", "name": "_name", "type": "string" }, + { "internalType": "string", "name": "_symbol", "type": "string" }, + { "internalType": "uint8", "name": "_decimals", "type": "uint8" }, + { + "internalType": "uint256", + "name": "total_supply_whole_tokens", + "type": "uint256" + }, + { "internalType": "uint256", "name": "faucet_amount", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "addedValue", "type": "uint256" } + ], + "name": "increaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isOwner", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [], + "name": "kill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function", + "constant": true + }, + { + "inputs": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "faucet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "issue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "internalType": "address", + "name": "bridge_address", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "admin_deposit_single", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "internalType": "address", + "name": "bridge_address", + "type": "address" + }, + { + "internalType": "bytes32[]", + "name": "vega_public_keys", + "type": "bytes32[]" + } + ], + "name": "admin_deposit_bulk", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/libs/smart-contracts/src/abis/erc20_bridge_abi.json b/libs/smart-contracts/src/abis/erc20_bridge_abi.json new file mode 100644 index 000000000..27b901a26 --- /dev/null +++ b/libs/smart-contracts/src/abis/erc20_bridge_abi.json @@ -0,0 +1,309 @@ +[ + { + "inputs": [ + { + "internalType": "address payable", + "name": "erc20_asset_pool", + "type": "address" + }, + { + "internalType": "address", + "name": "multisig_control", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset_source", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "new_maximum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "Asset_Deposit_Maximum_Set", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset_source", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "new_minimum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "Asset_Deposit_Minimum_Set", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user_address", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "asset_source", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "Asset_Deposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset_source", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vega_asset_id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "Asset_Listed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset_source", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "Asset_Removed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user_address", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "asset_source", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "Asset_Withdrawn", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "deposit_asset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "vega_asset_id", "type": "bytes32" } + ], + "name": "get_asset_source", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" } + ], + "name": "get_deposit_maximum", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" } + ], + "name": "get_deposit_minimum", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "get_multisig_control_address", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" } + ], + "name": "get_vega_asset_id", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" } + ], + "name": "is_asset_listed", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" }, + { "internalType": "bytes32", "name": "vega_asset_id", "type": "bytes32" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "bytes", "name": "signatures", "type": "bytes" } + ], + "name": "list_asset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "bytes", "name": "signatures", "type": "bytes" } + ], + "name": "remove_asset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" }, + { + "internalType": "uint256", + "name": "maximum_amount", + "type": "uint256" + }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "bytes", "name": "signatures", "type": "bytes" } + ], + "name": "set_deposit_maximum", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" }, + { + "internalType": "uint256", + "name": "minimum_amount", + "type": "uint256" + }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "bytes", "name": "signatures", "type": "bytes" } + ], + "name": "set_deposit_minimum", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset_source", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "bytes", "name": "signatures", "type": "bytes" } + ], + "name": "withdraw_asset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/libs/smart-contracts/src/abis/staking_abi.json b/libs/smart-contracts/src/abis/staking_abi.json new file mode 100644 index 000000000..d8a16aa65 --- /dev/null +++ b/libs/smart-contracts/src/abis/staking_abi.json @@ -0,0 +1,203 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "Stake_Deposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "Stake_Removed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "Stake_Transferred", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "stake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "remove_stake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "new_address", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "transfer_stake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "staking_token", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "stake_balance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "total_staked", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/libs/smart-contracts/src/abis/vega_token_abi.json b/libs/smart-contracts/src/abis/vega_token_abi.json new file mode 100644 index 000000000..394512f12 --- /dev/null +++ b/libs/smart-contracts/src/abis/vega_token_abi.json @@ -0,0 +1,214 @@ +[ + { + "inputs": [ + { "internalType": "uint256", "name": "total_supply_", "type": "uint256" }, + { + "internalType": "uint256", + "name": "mint_lock_expiry_", + "type": "uint256" + }, + { "internalType": "string", "name": "name_", "type": "string" }, + { "internalType": "string", "name": "symbol_", "type": "string" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "new_controller", + "type": "address" + } + ], + "name": "Controller_Changed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "new_controller", "type": "address" } + ], + "name": "change_controller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "controller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "addedValue", "type": "uint256" } + ], + "name": "increaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "mint_and_issue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "mint_lock_expiry", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/libs/smart-contracts/src/abis/vesting_abi.json b/libs/smart-contracts/src/abis/vesting_abi.json new file mode 100644 index 000000000..24de2c52f --- /dev/null +++ b/libs/smart-contracts/src/abis/vesting_abi.json @@ -0,0 +1,717 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "token_v1_address", + "type": "address" + }, + { + "internalType": "address", + "name": "token_v2_address", + "type": "address" + }, + { + "internalType": "address[]", + "name": "old_addresses", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "new_addresses", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "new_controller", + "type": "address" + } + ], + "name": "Controller_Set", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Issuer_Permitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "issuer", + "type": "address" + } + ], + "name": "Issuer_Revoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "Stake_Deposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "Stake_Removed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "Stake_Transferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Tranche_Balance_Added", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Tranche_Balance_Removed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cliff_start", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "Tranche_Created", + "type": "event" + }, + { + "inputs": [], + "name": "accuracy_scale", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "address_migration", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "assisted_withdraw_from_tranche", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "controller", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "cliff_start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "create_tranche", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "default_tranche_id", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + } + ], + "name": "get_tranche_balance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + } + ], + "name": "get_vested_for_tranche", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "issue_into_tranche", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "move_into_tranche", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "issuer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "permit_issuer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "permitted_issuance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "remove_stake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "issuer", + "type": "address" + } + ], + "name": "revoke_issuer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "new_controller", + "type": "address" + } + ], + "name": "set_controller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "stake_balance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "vega_public_key", + "type": "bytes32" + } + ], + "name": "stake_tokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "staking_token", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "total_locked", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "total_staked", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tranche_count", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "name": "tranches", + "outputs": [ + { + "internalType": "uint256", + "name": "cliff_start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "user_stats", + "outputs": [ + { + "internalType": "uint256", + "name": "total_in_all_tranches", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lien", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "user_total_all_tranches", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "v1_address", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "v1_migrated", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "v2_address", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "tranche_id", + "type": "uint8" + } + ], + "name": "withdraw_from_tranche", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/libs/smart-contracts/src/config/ethereum.ts b/libs/smart-contracts/src/config/ethereum.ts new file mode 100644 index 000000000..49675ba05 --- /dev/null +++ b/libs/smart-contracts/src/config/ethereum.ts @@ -0,0 +1,126 @@ +import { Networks } from './vega'; + +const customVegaTokenAddress = process.env.CUSTOM_TOKEN_ADDRESS as string; +const customClaimAddress = process.env.CUSTOM_CLAIM_ADDRESS as string; +const customLockedAddress = process.env.CUSTOM_LOCKED_ADDRESS as string; +const customVestingAddress = process.env.CUSTOM_VESTING_ADDRESS as string; +const customStakingBridge = process.env.CUSTOM_STAKING_BRIDGE as string; +const customErc20Bridge = process.env.CUSTOM_ERC20_BRIDGE as string; + +export type EthereumChainId = '0x1' | '0x3' | '0x4' | '0x5' | '0x2a'; + +export type EthereumChainName = + | 'Mainnet' + | 'Ropsten' + | 'Rinkeby' + | 'Goerli' + | 'Kovan'; + +export const EthereumChainNames: Record = { + '0x1': 'Mainnet', + '0x3': 'Ropsten', + '0x4': 'Rinkeby', + '0x5': 'Goerli', + '0x2a': 'Kovan', +}; + +export const EthereumChainIds: Record = { + Mainnet: '0x1', + Ropsten: '0x3', + Rinkeby: '0x4', + Goerli: '0x5', + Kovan: '0x2a', +}; + +export const ChainIdMap: Record = { + '0x1': 1, + '0x3': 3, + '0x4': 4, + '0x5': 5, + '0x2a': 42, +}; +interface VegaContracts { + vestingAddress: string; + vegaTokenAddress: string; + claimAddress: string; + lockedAddress: string; + stakingBridge: string; + erc20Bridge: string; +} + +export const EnvironmentConfig: { [key in Networks]: VegaContracts } = { + [Networks.CUSTOM]: { + vegaTokenAddress: customVegaTokenAddress, + claimAddress: customClaimAddress, + lockedAddress: customLockedAddress, + vestingAddress: customVestingAddress, + stakingBridge: customStakingBridge, + erc20Bridge: customErc20Bridge, + }, + [Networks.DEVNET]: { + vegaTokenAddress: '0xc93137f9F4B820Ca85FfA3C7e84cCa6Ebc7bB517', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', + lockedAddress: '0x0', + vestingAddress: '0xd1216AAb948f5FC706Df73df6d71c64CcaA8550a', + stakingBridge: '0xf2cD5C8b8c52f96293338A0AF463a0Bfc602D5bc', + erc20Bridge: '0xE43013C3c2A134AB3782ADEb258669A8566DAD57', + }, + [Networks.STAGNET]: { + vegaTokenAddress: '0x547cbA83a7eb82b546ee5C7ff0527F258Ba4546D', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error + lockedAddress: '0x0', // TODO not deployed to this env + vestingAddress: '0xfCe6eB272D3d4146A96bC28de71212b327F575fa', + stakingBridge: '0x7D88CD817227D599815d407D929af18Bb8D57176', + erc20Bridge: '0xc0835e6dEf177F8ba2561C4e4216827A3798c6B9', + }, + [Networks.STAGNET2]: { + vegaTokenAddress: '0xd8fa193B93a179DdCf51FFFDe5320E0872cdcf44', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error + lockedAddress: '0x0', // TODO not deployed to this env + vestingAddress: '0x9F10cBeEf03A564Fb914c2010c0Cd55E9BB11406', + stakingBridge: '0x7896C9491962D5839783CB6e0492ECebd34Bb35F', + erc20Bridge: '0xffC1eb64e22fd5E29816c633eE84088EEEe879E5', + }, + [Networks.TESTNET]: { + vegaTokenAddress: '0xDc335304979D378255015c33AbFf09B60c31EBAb', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error + lockedAddress: '0x0', // TODO not deployed to this env + vestingAddress: '0xe2deBB240b43EDfEBc9c38B67c0894B9A92Bf07c', + stakingBridge: '0xF5A3830F002BE78dd801214F5316b677E0355c60', + erc20Bridge: '0xF009C66c6afC9661143fD7cE1eDb02c1961a6510', + }, + [Networks.MAINNET]: { + vegaTokenAddress: '0xcB84d72e61e383767C4DFEb2d8ff7f4FB89abc6e', + claimAddress: '0x0ee1fb382caf98e86e97e51f9f42f8b4654020f3', + lockedAddress: '0x78344c7305d73a7a0ac3c94cd9960f4449a1814e', + vestingAddress: '0x23d1bFE8fA50a167816fBD79D7932577c06011f4', + stakingBridge: '0x195064D33f09e0c42cF98E665D9506e0dC17de68', + erc20Bridge: '0xCd403f722b76366f7d609842C589906ca051310f', + }, +}; + +// No concept of dev/staging/test for these right now. +export const RewardsAddresses = { + [EthereumChainIds.Mainnet]: { + 'SushiSwap VEGA/ETH': '0x285de24077440c53b1661287D170e3ae22de0a44', + 'SushiSwap VEGA/USDC': '0x49407c243c26f109b3c77c41dd83742164c20b5f', + } as { [key: string]: string }, + [EthereumChainIds.Ropsten]: { + 'SushiSwap VEGA/ETH': '0xa93dd6912897c5fe8503a82234d829bc7905714b', + 'SushiSwap VEGA/USDC': '0xa93dd6912897c5fe8503a82234d829bc7905714b', + } as { [key: string]: string }, +}; + +export const RewardsPoolAddresses = { + [EthereumChainIds.Mainnet]: { + '0x285de24077440c53b1661287D170e3ae22de0a44': + '0x29c827ce49accf68a1a278c67c9d30c52fbbc348', + '0x49407c243c26f109b3c77c41dd83742164c20b5f': + '0x42b7B8f8F83fA5cbf0176f8c24Ad51EbcD4B5F17', + } as { [key: string]: string }, + [EthereumChainIds.Ropsten]: { + // Only one deployed to this environment + '0xa93dd6912897c5fe8503a82234d829bc7905714b': + '0x29c827ce49accf68a1a278c67c9d30c52fbbc348', + } as { [key: string]: string }, +}; diff --git a/libs/smart-contracts/src/config/index.ts b/libs/smart-contracts/src/config/index.ts new file mode 100644 index 000000000..51cb66024 --- /dev/null +++ b/libs/smart-contracts/src/config/index.ts @@ -0,0 +1,2 @@ +export * from './ethereum'; +export * from './vega'; diff --git a/libs/smart-contracts/src/config/vega.ts b/libs/smart-contracts/src/config/vega.ts new file mode 100644 index 000000000..038489d6e --- /dev/null +++ b/libs/smart-contracts/src/config/vega.ts @@ -0,0 +1,8 @@ +export enum Networks { + CUSTOM = 'CUSTOM', + TESTNET = 'TESTNET', + STAGNET = 'STAGNET', + STAGNET2 = 'STAGNET2', + DEVNET = 'DEVNET', + MAINNET = 'MAINNET', +} diff --git a/libs/smart-contracts/src/contracts/base-contract.ts b/libs/smart-contracts/src/contracts/base-contract.ts new file mode 100644 index 000000000..954aaa452 --- /dev/null +++ b/libs/smart-contracts/src/contracts/base-contract.ts @@ -0,0 +1,73 @@ +import type { ethers } from 'ethers'; +import type { TxData } from '.'; + +export class BaseContract { + public signer: ethers.Signer | null = null; + public provider: ethers.providers.Provider; + public transactions: TxData[] = []; + public listeners: ((transactions: TxData[]) => void)[] = []; + + constructor(provider: ethers.providers.Provider, signer?: ethers.Signer) { + this.provider = provider; + this.signer = signer || null; + } + + async handleEvent(event: ethers.Event, requiredConfirmations = 1) { + const tx = await event.getTransaction(); + // start tracking transaction if its not already in the transactions array + const existing = this.transactions.find((t) => t.tx.hash === tx.hash); + if (!existing) { + this.trackTransaction(tx, requiredConfirmations); + } + } + + async trackTransaction( + tx: ethers.providers.TransactionResponse, + requiredConfirmations = 1 + ) { + this.mergeTransaction({ + tx, + receipt: null, + pending: true, + requiredConfirmations, + }); + + let receipt = null; + + for (let i = 1; i <= requiredConfirmations; i++) { + receipt = await tx.wait(i); + this.mergeTransaction({ + tx, + receipt, + pending: true, + requiredConfirmations, + }); + } + + this.mergeTransaction({ + tx, + receipt, + pending: false, + requiredConfirmations, + }); + } + + private mergeTransaction(tx: TxData) { + this.transactions = [ + // Replace any existing transaction in the array with this one + ...this.transactions.filter((t) => t.tx.hash !== tx.tx.hash), + tx, + ]; + this.emit(); + } + + emit() { + this.listeners.forEach((ln) => { + ln(this.transactions); + }); + } + + listen(cb: (txs: TxData[]) => void) { + this.listeners.push(cb); + } +} diff --git a/libs/smart-contracts/src/contracts/erc20-token.ts b/libs/smart-contracts/src/contracts/erc20-token.ts new file mode 100644 index 000000000..64f63b6c4 --- /dev/null +++ b/libs/smart-contracts/src/contracts/erc20-token.ts @@ -0,0 +1,111 @@ +import type { BigNumber as EthersBigNumber } from 'ethers'; +import { ethers } from 'ethers'; +import erc20Abi from '../abis/erc20_abi.json'; +import erc20AbiFaucet from '../abis/erc20_abi_faucet.json'; +import BigNumber from 'bignumber.js'; +import { addDecimal, removeDecimal } from '../utils'; +import { BaseContract } from './base-contract'; + +export class ERC20Token extends BaseContract { + public contract: ethers.Contract; + public dp: Promise; + private faucetable: boolean; + + constructor( + address: string, + provider: ethers.providers.Web3Provider, + signer?: ethers.Signer, + faucetable = false + ) { + super(provider, signer); + + this.faucetable = faucetable; + const contract = new ethers.Contract( + address, + faucetable ? erc20AbiFaucet : erc20Abi, + signer || provider + ); + this.contract = contract; + this.dp = (async () => { + const val = await contract.decimals(); + return Number(val); + })(); + } + + /** Gets Vega token total supply */ + async totalSupply(): Promise { + const res: EthersBigNumber = await this.contract.totalSupply(); + const value = addDecimal(new BigNumber(res.toString()), await this.dp); + return value; + } + + /** Gets number tokens an Ethereum account owns */ + async balanceOf(address: string): Promise { + const res: EthersBigNumber = await this.contract.balanceOf(address); + const value = addDecimal(new BigNumber(res.toString()), await this.dp); + return value; + } + + async transfer( + from: string, + to: string, + amount: BigNumber, + confirmations = 1 + ) { + const value = removeDecimal(amount, await this.dp).toString(); + const tx = await this.contract.transfer(from, to, value); + this.trackTransaction(tx, confirmations); + return tx; + } + + async transferFrom( + sender: string, + recipient: string, + amount: BigNumber, + confirmations = 1 + ) { + const value = removeDecimal(amount, await this.dp).toString(); + const tx = await this.contract.transferFrom(sender, recipient, value); + this.trackTransaction(tx, confirmations); + return tx; + } + + /** Gets Ethereum account's permitted allowance */ + async allowance(address: string, spender: string): Promise { + const res: EthersBigNumber = await this.contract.allowance( + address, + spender + ); + const value = addDecimal(new BigNumber(res.toString()), await this.dp); + return value; + } + + /** Executs contracts approve function */ + async approve( + spender: string, + confirmations = 1 + ): Promise { + const amount = removeDecimal( + new BigNumber(Number.MAX_SAFE_INTEGER), + await this.dp + ).toString(); + const tx = await this.contract.approve(spender, amount); + this.trackTransaction(tx, confirmations); + return tx; + } + + async faucet(confirmations = 1): Promise { + if (!this.faucetable) { + throw new Error('Faucet function can not be called on this contract'); + } + const tx = await this.contract.faucet(); + this.trackTransaction(tx, confirmations); + return tx; + } + + /** Gets number of decimals used by token */ + async decimals(): Promise { + const res: number = await this.contract.decimals(); + return Number(res); + } +} diff --git a/libs/smart-contracts/src/contracts/index.ts b/libs/smart-contracts/src/contracts/index.ts new file mode 100644 index 000000000..86140f0e5 --- /dev/null +++ b/libs/smart-contracts/src/contracts/index.ts @@ -0,0 +1,8 @@ +export * from './stake-helpers'; +export * from './tranche-helpers'; +export * from './vega-claim'; +export * from './vega-erc20-bridge'; +export * from './vega-staking'; +export * from './vega-vesting'; +export * from './vega-web3-types'; +export * from './erc20-token'; diff --git a/libs/smart-contracts/src/contracts/stake-helpers.ts b/libs/smart-contracts/src/contracts/stake-helpers.ts new file mode 100644 index 000000000..8b924da6f --- /dev/null +++ b/libs/smart-contracts/src/contracts/stake-helpers.ts @@ -0,0 +1,40 @@ +import type { ethers } from 'ethers'; + +import BigNumber from 'bignumber.js'; +import { addDecimal } from '../utils/decimals'; + +export function combineStakeEventsByVegaKey( + events: ethers.Event[], + decimals: number +): { [vegaKey: string]: BigNumber } { + const res = events.reduce((obj, e) => { + const vegaKey = e.args?.vega_public_key; + const amount = parseEventAmount(e, decimals); + const isDeposit = e.event === 'Stake_Deposited'; + const isRemove = e.event === 'Stake_Removed'; + + if (!isDeposit && !isRemove) return obj; + + if (Object.prototype.hasOwnProperty.call(obj, vegaKey)) { + if (isDeposit) { + obj[vegaKey] = obj[vegaKey].plus(amount); + } else { + obj[vegaKey] = obj[vegaKey].minus(amount); + } + } else { + if (isDeposit) { + obj[vegaKey] = amount; + } else { + obj[vegaKey] = new BigNumber(0); + } + } + return obj; + }, {} as { [vegaKey: string]: BigNumber }); + + return res; +} + +function parseEventAmount(e: ethers.Event, decimals: number) { + const rawAmount = new BigNumber(e.args?.amount.toString() || 0); + return addDecimal(rawAmount, decimals); +} diff --git a/libs/smart-contracts/src/contracts/tranche-helpers.ts b/libs/smart-contracts/src/contracts/tranche-helpers.ts new file mode 100644 index 000000000..2d0a26d9b --- /dev/null +++ b/libs/smart-contracts/src/contracts/tranche-helpers.ts @@ -0,0 +1,165 @@ +import BigNumber from 'bignumber.js'; +import type { ethers } from 'ethers'; +import uniq from 'lodash/uniq'; +import { addDecimal } from '../utils/decimals'; + +import type { Tranche, TrancheUser } from './vega-web3-types'; +import { TrancheEvents } from './vega-web3-types'; + +export function createUserTransactions( + events: ethers.Event[], + decimals: number +) { + return events.map((event) => { + return { + amount: addDecimal( + new BigNumber(event.args?.amount.toString()), + decimals + ), + user: event.args?.user, + tranche_id: event.args?.tranche_id, + tx: event.transactionHash, + }; + }); +} + +export function getUsersInTranche( + balanceAddedEvents: ethers.Event[], + balanceRemovedEvents: ethers.Event[], + addresses: string[], + decimals: number +): TrancheUser[] { + return addresses.map((address) => { + const userDeposits = balanceAddedEvents.filter( + (event) => event.args?.user === address + ); + const userWithdraws = balanceRemovedEvents.filter( + (event) => event.args?.user === address + ); + const deposits = createUserTransactions(userDeposits, decimals); + const withdrawals = createUserTransactions(userWithdraws, decimals); + const total_tokens = deposits.reduce( + (pre, cur) => pre.plus(cur.amount), + new BigNumber(0) + ); + const withdrawn_tokens = withdrawals.reduce( + (pre, cur) => pre.plus(cur.amount), + new BigNumber(0) + ); + const remaining_tokens = total_tokens.minus(withdrawn_tokens); + + return { + address, + deposits, + withdrawals, + total_tokens, + withdrawn_tokens, + remaining_tokens, + }; + }); +} + +export function sumFromEvents(events: ethers.Event[], decimals: number) { + const amounts = events.map((e) => + addDecimal(new BigNumber(e.args?.amount.toString()), decimals) + ); + // Start with a 0 so if there are none there is no NaN + return BigNumber.sum.apply(null, [new BigNumber(0), ...amounts]); +} + +export function getLockedAmount( + totalAdded: BigNumber, + cliffStart: number, + trancheDuration: number +) { + let amount = new BigNumber(0); + const ts = Math.round(new Date().getTime() / 1000); + const tranche_progress = (ts - cliffStart) / trancheDuration; + + if (tranche_progress < 0) { + amount = totalAdded; + } else if (tranche_progress < 1) { + amount = totalAdded.times(1 - tranche_progress); + } + + return amount; +} + +export function createTransactions(events: ethers.Event[], decimals: number) { + return events.map((event) => { + return { + amount: addDecimal( + new BigNumber(event.args?.amount.toString()), + decimals + ), + user: event.args?.user, + tx: event.transactionHash, + }; + }); +} + +export function getTranchesFromHistory( + createEvents: ethers.Event[], + addEvents: ethers.Event[], + removeEvents: ethers.Event[], + decimals: number +): Tranche[] { + return createEvents.map((event) => { + const tranche_id = event.args?.tranche_id; + const balanceAddedEvents = addEvents.filter( + (e) => + e.event === TrancheEvents.BalanceAdded && + e.args?.tranche_id === tranche_id + ); + const balanceRemovedEvents = removeEvents.filter( + (e) => + e.event === TrancheEvents.BalanceRemoved && + e.args?.tranche_id === tranche_id + ); + + //get tranche start and end dates + const tranche_duration = event.args?.duration; + const cliff_start = event.args?.cliff_start; + const tranche_start = new Date(cliff_start.mul(1000).toNumber()); + const tranche_end = new Date( + cliff_start.add(tranche_duration).mul(1000).toNumber() + ); + + // get added and removed values + const total_added = sumFromEvents(balanceAddedEvents, decimals); + const total_removed = sumFromEvents(balanceRemovedEvents, decimals); + // get locked amount + const locked_amount = getLockedAmount( + total_added, + cliff_start, + tranche_duration + ); + + // get all deposits and withdrawals + const deposits = createTransactions(balanceAddedEvents, decimals); + const withdrawals = createTransactions(balanceRemovedEvents, decimals); + + // get all users + const uniqueAddresses = uniq( + balanceAddedEvents.map((event) => event.args?.user) + ); + const users = getUsersInTranche( + balanceAddedEvents, + balanceRemovedEvents, + uniqueAddresses, + decimals + ); + + return { + tranche_id: parseInt(tranche_id), + tranche_start, + tranche_end, + total_added, + total_removed, + locked_amount, + deposits, + withdrawals, + users, + }; + }); +} diff --git a/libs/smart-contracts/src/contracts/vega-claim.ts b/libs/smart-contracts/src/contracts/vega-claim.ts new file mode 100644 index 000000000..6ca34ec7c --- /dev/null +++ b/libs/smart-contracts/src/contracts/vega-claim.ts @@ -0,0 +1,156 @@ +import type BigNumber from 'bignumber.js'; +import { ethers } from 'ethers'; +import { EnvironmentConfig } from '../config/ethereum'; +import type { Networks } from '../config/vega'; +import claimAbi from '../abis/claim_abi.json'; +import tokenAbi from '../abis/vega_token_abi.json'; +import { asciiToHex, removeDecimal } from '../utils'; +import { BaseContract } from './base-contract'; + +export const UNSPENT_CODE = '0x0000000000000000000000000000000000000000'; +export const SPENT_CODE = '0x0000000000000000000000000000000000000001'; + +/** + * Example: + * ``` + * const provider = new Web3.providers.HttpProvider( + * "https://ropsten.infura.io/v3/5aff9e61ad844bcf982d0d0c3f1d29f1" + * ); + * const web3 = new Web3(provider); + * + * // Ropsten address + * const contract = new VegaClaim(web3, "0xAf5dC1772714b2F4fae3b65eb83100f1Ea677b21") + * contract.isCountryBlocked("US").then(console.log) + * contract.isClaimValid({ claimCode: "0x...", expiry: 0, nonce: "0x00", account: "0x00" }) + * ``` + */ +export class VegaClaim extends BaseContract { + public contract: ethers.Contract; + public tokenContract: ethers.Contract; + public dp: Promise; + + constructor( + network: Networks, + provider: ethers.providers.Web3Provider, + signer?: ethers.Signer + ) { + super(provider, signer); + + this.contract = new ethers.Contract( + EnvironmentConfig[network].claimAddress, + claimAbi, + this.signer || this.provider + ); + + const tokenContract = new ethers.Contract( + EnvironmentConfig[network].vegaTokenAddress, + tokenAbi, + this.signer || this.provider + ); + this.tokenContract = tokenContract; + + this.dp = (async () => { + const val = await tokenContract.decimals(); + return Number(val); + })(); + } + + /** Execute contracts commit_untargeted function */ + async commit( + s: string, + confirmations = 1 + ): Promise { + const tx = await this.contract.commit_untargeted(s); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** + * Perform the final claim. Automatically switches between targeted and + * untargeted claims. However, for untargeted ones, it's assumed that commit + * was performed and mined beforehand + * @return {Promise} + */ + public async claim( + { + amount, + tranche, + expiry, + target, + country, + v, + r, + s, + }: { + amount: BigNumber; + tranche: number; + expiry: number; + target?: string; + country: string; + v: number; + r: string; + s: string; + }, + confirmations = 1 + ): Promise { + const convertedAmount = removeDecimal(amount, await this.dp).toString(); + const tx = await this.contract[ + target != null ? 'claim_targeted' : 'claim_untargeted' + ]( + ...[ + { r, s, v }, + { + amount: convertedAmount, + tranche, + expiry, + }, + asciiToHex(country), + target, + ].filter(Boolean) + ); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** + * Check if this code was already committed to by this account + * @return {Promise} + */ + async isCommitted({ s }: { s: string }): Promise { + return await this.contract.commitments(s); + } + + /** + * Checks if a code is past its' expiry date + * @param expiry Expiry of the code + * @returns Promise + */ + async isExpired(expiry: number): Promise { + return expiry < (await this.provider.getBlock('latest')).timestamp; + } + + /** + * Utility method to check if the nonce has already been used. If it has the code has already been claimed. + * @param s The s part of the signature + * @return {string} + */ + async isUsed(s: string): Promise { + return (await this.contract.commitments(s)) === SPENT_CODE; + } + + /** + * Check if country is blocked. country must be the two letter ISO code + * @param {string} country 2 letter ISO code + * @return {Promise} + */ + async isCountryBlocked(country: string): Promise { + const isAllowed = await this.contract.allowed_countries( + asciiToHex(country) + ); + return !isAllowed; + } +} diff --git a/libs/smart-contracts/src/contracts/vega-erc20-bridge.ts b/libs/smart-contracts/src/contracts/vega-erc20-bridge.ts new file mode 100644 index 000000000..3926658ae --- /dev/null +++ b/libs/smart-contracts/src/contracts/vega-erc20-bridge.ts @@ -0,0 +1,109 @@ +import type { BigNumber as EthersBigNumber } from 'ethers'; +import { ethers } from 'ethers'; +import { EnvironmentConfig } from '../config/ethereum'; +import type { Networks } from '../config/vega'; + +import erc20BridgeAbi from '../abis/erc20_bridge_abi.json'; +import { BaseContract } from './base-contract'; +import BigNumber from 'bignumber.js'; +import { addDecimal } from '../utils'; + +export class VegaErc20Bridge extends BaseContract { + private contract: ethers.Contract; + + constructor( + network: Networks, + provider: ethers.providers.Web3Provider, + signer?: ethers.Signer + ) { + super(provider, signer); + this.contract = new ethers.Contract( + EnvironmentConfig[network].erc20Bridge, + erc20BridgeAbi, + this.signer || this.provider + ); + } + + /** Executes contracts withdraw_asset function */ + async withdraw( + approval: { + assetSource: string; + amount: string; + nonce: string; + signatures: string; + targetAddress: string; + }, + confirmations = 1 + ): Promise { + const tx = await this.contract.withdraw_asset( + approval.assetSource, + approval.amount, // No need to remove decimals as this value is already set and not manipulated by the user + approval.targetAddress, + approval.nonce, + approval.signatures + ); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + async depositAsset( + assetSource: string, + amount: string, + vegaPublicKey: string, + confirmations = 1 + ) { + const tx = await this.contract.deposit_asset( + assetSource, + amount, + vegaPublicKey + ); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + async getAssetSource(vegaAssetId: string): Promise { + const res = await this.contract.get_asset_source(vegaAssetId); + return res; + } + + async getDepositMaximum( + assetSource: string, + decimals: number + ): Promise { + const res: EthersBigNumber = await this.contract.get_deposit_maximum( + assetSource + ); + const value = addDecimal(new BigNumber(res.toString()), decimals); + return value; + } + + async getDepositMinimum( + assetSource: string, + decimals: number + ): Promise { + const res: EthersBigNumber = await this.contract.get_deposit_minimum( + assetSource + ); + const value = addDecimal(new BigNumber(res.toString()), decimals); + return value; + } + + async getMultisigControlAddress(): Promise { + const res = await this.contract.get_multisig_control_address(); + return res; + } + + async getVegaAssetId(): Promise { + const res = await this.contract.get_vega_asset_id(); + return res; + } + + async isAssetListed(assetSource: string): Promise { + const res = await this.contract.is_asset_listed(assetSource); + return res; + } +} diff --git a/libs/smart-contracts/src/contracts/vega-staking.ts b/libs/smart-contracts/src/contracts/vega-staking.ts new file mode 100644 index 000000000..b9c86ef45 --- /dev/null +++ b/libs/smart-contracts/src/contracts/vega-staking.ts @@ -0,0 +1,131 @@ +import type { BigNumber as EthersBigNumber } from 'ethers'; +import { ethers } from 'ethers'; +import stakingAbi from '../abis/staking_abi.json'; +import tokenAbi from '../abis/vega_token_abi.json'; +import { combineStakeEventsByVegaKey } from './stake-helpers'; +import BigNumber from 'bignumber.js'; +import { BaseContract } from './base-contract'; +import { EnvironmentConfig } from '../config/ethereum'; +import type { Networks } from '../config/vega'; +import { addDecimal, hexadecimalify, removeDecimal } from '../utils'; + +export class VegaStaking extends BaseContract { + public contract: ethers.Contract; + public tokenContract: ethers.Contract; + public dp: Promise; + + constructor( + network: Networks, + provider: ethers.providers.Web3Provider, + signer?: ethers.Signer + ) { + super(provider, signer); + + const tokenContract = new ethers.Contract( + EnvironmentConfig[network].vegaTokenAddress, + tokenAbi, + this.signer || this.provider + ); + this.tokenContract = tokenContract; + + this.contract = new ethers.Contract( + EnvironmentConfig[network].stakingBridge, + stakingAbi, + this.signer || this.provider + ); + + this.dp = (async () => { + const val = await tokenContract.decimals(); + return Number(val); + })(); + } + + /** Executes staking contracts stake function */ + async addStake( + amount: BigNumber, + vegaKey: string, + confirmations = 1 + ): Promise { + const convertedAmount = removeDecimal(amount, await this.dp).toString(); + + const tx = await this.contract.stake( + convertedAmount, + hexadecimalify(vegaKey) + ); + + // store and track the transaction in BaseContract + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** Executes staking contracts remove_stake function */ + async removeStake( + amount: BigNumber, + vegaKey: string, + confirmations = 1 + ): Promise { + const convertedAmount = removeDecimal(amount, await this.dp).toString(); + + const tx = await this.contract.remove_stake( + convertedAmount, + hexadecimalify(vegaKey) + ); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** Executes staking contracts transfer_stake function */ + async transferStake( + amount: BigNumber, + newAddress: string, + vegaKey: string, + confirmations = 1 + ): Promise { + const convertedAmount = removeDecimal(amount, await this.dp).toString(); + + const tx = await this.contract.transfer_stake( + convertedAmount, + newAddress, + hexadecimalify(vegaKey) + ); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** Returns the amount staked for given Vega public key */ + async stakeBalance(address: string, vegaKey: string): Promise { + const res: EthersBigNumber = await this.contract.stake_balance( + address, + hexadecimalify(vegaKey) + ); + const value = addDecimal(new BigNumber(res.toString()), await this.dp); + return value; + } + + /** Returns the total amount currently staked */ + async totalStaked(): Promise { + const res: EthersBigNumber = await this.contract.total_staked(); + const value = addDecimal(new BigNumber(res.toString()), await this.dp); + return value; + } + + /** Returns amounts staked across all Vega keys for single Ethereum account */ + async userTotalStakedByVegaKey( + ethereumAccount: string + ): Promise<{ [vegaKey: string]: BigNumber }> { + const addFilter = this.contract.filters.Stake_Deposited(ethereumAccount); + const removeFilter = this.contract.filters.Stake_Removed(ethereumAccount); + const addEvents = await this.contract.queryFilter(addFilter); + const removeEvents = await this.contract.queryFilter(removeFilter); + const res = combineStakeEventsByVegaKey( + [...addEvents, ...removeEvents], + await this.dp + ); + return res; + } +} diff --git a/libs/smart-contracts/src/contracts/vega-vesting.ts b/libs/smart-contracts/src/contracts/vega-vesting.ts new file mode 100644 index 000000000..425672466 --- /dev/null +++ b/libs/smart-contracts/src/contracts/vega-vesting.ts @@ -0,0 +1,182 @@ +import BigNumber from 'bignumber.js'; +import type { BigNumber as EthersBigNumber } from 'ethers'; +import { ethers } from 'ethers'; +import { EnvironmentConfig } from '../config/ethereum'; +import type { Networks } from '../config/vega'; +import vestingAbi from '../abis/vesting_abi.json'; +import tokenAbi from '../abis/vega_token_abi.json'; +import { BaseContract } from './base-contract'; +import { combineStakeEventsByVegaKey } from './stake-helpers'; +import { getTranchesFromHistory } from './tranche-helpers'; +import type { Tranche } from './vega-web3-types'; +import { addDecimal, hexadecimalify, removeDecimal } from '../utils'; + +export class VegaVesting extends BaseContract { + public contract: ethers.Contract; + public tokenContract: ethers.Contract; + public dp: Promise; + + constructor( + network: Networks, + provider: ethers.providers.Web3Provider, + signer?: ethers.Signer + ) { + super(provider, signer); + + const tokenContract = new ethers.Contract( + EnvironmentConfig[network].vegaTokenAddress, + tokenAbi, + this.signer || this.provider + ); + this.tokenContract = tokenContract; + + this.contract = new ethers.Contract( + EnvironmentConfig[network].vestingAddress, + vestingAbi, + this.signer || this.provider + ); + + this.dp = (async () => { + const val = await tokenContract.decimals(); + return Number(val); + })(); + } + + /** Executes vesting contracts stake_tokens function */ + async addStake( + amount: BigNumber, + vegaKey: string, + confirmations = 1 + ): Promise { + const convertedAmount = removeDecimal(amount, await this.dp).toString(); + + const tx = await this.contract.stake_tokens( + convertedAmount, + hexadecimalify(vegaKey) + ); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** Executes vesting contracts remove_stake function */ + async removeStake( + amount: BigNumber, + vegaKey: string, + confirmations = 1 + ): Promise { + const convertedAmount = removeDecimal(amount, await this.dp).toString(); + + const tx = await this.contract.remove_stake( + convertedAmount, + hexadecimalify(vegaKey) + ); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** Returns the amount staked for a given Vega public key */ + async stakeBalance(address: string, vegaKey: string): Promise { + const res: EthersBigNumber = await this.contract.stake_balance( + address, + hexadecimalify(vegaKey) + ); + const value = addDecimal(new BigNumber(res.toString()), await this.dp); + return value; + } + + /** Returns the total amount currently staked */ + async totalStaked(): Promise { + const res: EthersBigNumber = await this.contract.total_staked(); + const value = addDecimal(new BigNumber(res.toString()), await this.dp); + return value; + } + + /** Returns the amount of locked tokens in the vesting contract */ + async getLien(address: string): Promise { + const { + lien, + }: { + lien: EthersBigNumber; + total_in_all_tranches: EthersBigNumber; + } = await this.contract.user_stats(address); + const value = addDecimal(new BigNumber(lien.toString()), await this.dp); + return value; + } + + /** Returns the amount a user has in a specific tranche */ + async userTrancheTotalBalance( + address: string, + tranche: number + ): Promise { + const amount: EthersBigNumber = await this.contract.get_tranche_balance( + address, + tranche + ); + const value = addDecimal(new BigNumber(amount.toString()), await this.dp); + return value; + } + + /** Returns vested amount for a given tranche */ + async userTrancheVestedBalance( + address: string, + tranche: number + ): Promise { + const amount: EthersBigNumber = await this.contract.get_vested_for_tranche( + address, + tranche + ); + const value = addDecimal(new BigNumber(amount.toString()), await this.dp); + return value; + } + + /** Returns the users total tokens across all tranches */ + async getUserBalanceAllTranches(account: string): Promise { + const amount: EthersBigNumber = await this.contract.user_total_all_tranches( + account + ); + const value = addDecimal(new BigNumber(amount.toString()), await this.dp); + return value; + } + + /** Gets all tranche data */ + async getAllTranches(): Promise { + const [created, added, removed] = await Promise.all([ + this.contract.queryFilter(this.contract.filters.Tranche_Created()), + this.contract.queryFilter(this.contract.filters.Tranche_Balance_Added()), + this.contract.queryFilter( + this.contract.filters.Tranche_Balance_Removed() + ), + ]); + const dp = await this.dp; + return getTranchesFromHistory(created, added, removed, dp); + } + + /** Executes contracts withdraw_from_tranche function */ + async withdrawFromTranche( + trancheId: number, + confirmations = 1 + ): Promise { + const tx = await this.contract.withdraw_from_tranche(trancheId); + + this.trackTransaction(tx, confirmations); + + return tx; + } + + /** Returns amounts staked across all Vega keys for single Ethereum account */ + async userTotalStakedByVegaKey(address: string) { + const addFilter = this.contract.filters.Stake_Deposited(address); + const removeFilter = this.contract.filters.Stake_Removed(address); + const addEvents = await this.contract.queryFilter(addFilter); + const removeEvents = await this.contract.queryFilter(removeFilter); + const res = combineStakeEventsByVegaKey( + [...addEvents, ...removeEvents], + await this.dp + ); + return res; + } +} diff --git a/libs/smart-contracts/src/contracts/vega-web3-types.ts b/libs/smart-contracts/src/contracts/vega-web3-types.ts new file mode 100644 index 000000000..8b5f5452b --- /dev/null +++ b/libs/smart-contracts/src/contracts/vega-web3-types.ts @@ -0,0 +1,83 @@ +import type { ethers } from 'ethers'; +import type BigNumber from 'bignumber.js'; + +export interface Tranche { + tranche_id: number; + tranche_start: Date; + tranche_end: Date; + total_added: BigNumber; + total_removed: BigNumber; + locked_amount: BigNumber; + deposits: Array; + withdrawals: Array; + users: Array; +} + +export interface TrancheDeposit { + amount: BigNumber; + user: string; + tx: string; +} + +export interface TrancheWithdrawal { + amount: BigNumber; + user: string; + tx: string; +} + +export interface TrancheUser { + address: string; + deposits: Array<{ + amount: BigNumber; + user: string; + tx: string; + tranche_id: number; + }>; + withdrawals: Array<{ + amount: BigNumber; + user: string; + tx: string; + tranche_id: number; + }>; + total_tokens: BigNumber; + withdrawn_tokens: BigNumber; + remaining_tokens: BigNumber; +} + +export enum TrancheEvents { + Created = 'Tranche_Created', + BalanceAdded = 'Tranche_Balance_Added', + BalanceRemoved = 'Tranche_Balance_Removed', +} + +export interface IVegaClaimData { + amount: BigNumber; + tranche: number; + expiry: number; + target?: string; +} + +export interface IVegaClaimSignature { + v: number; + r: string; + s: string; +} + +export interface IClaimTokenParams { + claim: IVegaClaimData; + signature: IVegaClaimSignature; + country: string | null; +} + +export interface EpochDetails { + id: string; + startSeconds: BigNumber; + endSeconds: BigNumber; +} + +export interface TxData { + tx: ethers.ContractTransaction; + receipt: ethers.ContractReceipt | null; + pending: boolean; + requiredConfirmations: number; +} diff --git a/libs/smart-contracts/src/index.ts b/libs/smart-contracts/src/index.ts new file mode 100644 index 000000000..4d801929f --- /dev/null +++ b/libs/smart-contracts/src/index.ts @@ -0,0 +1,3 @@ +export * from './config'; +export * from './contracts'; +export * from './utils'; diff --git a/libs/smart-contracts/src/utils/ascii-to-hex.ts b/libs/smart-contracts/src/utils/ascii-to-hex.ts new file mode 100644 index 000000000..606a6b2d4 --- /dev/null +++ b/libs/smart-contracts/src/utils/ascii-to-hex.ts @@ -0,0 +1,17 @@ +/** + * From: + * https://github.com/ChainSafe/web3.js/blob/436e77a8eaa061fbaa183a9f73ca590c2e1d7697/packages/web3-utils/src/index.js + */ +export const asciiToHex = (str: string) => { + if (!str) return '0x00'; + + let hex = ''; + + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i); + const n = code.toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return '0x' + hex; +}; diff --git a/libs/smart-contracts/src/utils/decimals.test.ts b/libs/smart-contracts/src/utils/decimals.test.ts new file mode 100644 index 000000000..3e4e42a44 --- /dev/null +++ b/libs/smart-contracts/src/utils/decimals.test.ts @@ -0,0 +1,22 @@ +import BigNumber from 'bignumber.js'; +import { addDecimal, removeDecimal } from './decimals'; + +test('Do not pad numbers with 0s when the number length is less than the specified DPs', () => { + expect(addDecimal(new BigNumber(10000), 10).toString()).toEqual('0.000001'); +}); + +test('Handles large numbers correctly', () => { + const claimCode = new BigNumber('20000000000000000000000000'); + const decimals = 18; + + const decimalised = addDecimal(claimCode, decimals); + expect(decimalised.toString()).toEqual('20000000'); + + const undecimalised = removeDecimal(claimCode, decimals); + expect(undecimalised.toString()).toEqual( + '20000000000000000000000000000000000000000000' + ); + + const mangled = removeDecimal(addDecimal(claimCode, decimals), decimals); + expect(mangled.toString()).toEqual('20000000000000000000000000'); +}); diff --git a/libs/smart-contracts/src/utils/decimals.ts b/libs/smart-contracts/src/utils/decimals.ts new file mode 100644 index 000000000..0654e6bb6 --- /dev/null +++ b/libs/smart-contracts/src/utils/decimals.ts @@ -0,0 +1,10 @@ +import BigNumber from 'bignumber.js'; + +BigNumber.config({ EXPONENTIAL_AT: 20000 }); + +export function addDecimal(value: BigNumber, decimals: number): BigNumber { + return value.dividedBy(Math.pow(10, decimals)).decimalPlaces(decimals); +} +export function removeDecimal(value: BigNumber, decimals: number): BigNumber { + return value.times(Math.pow(10, decimals)); +} diff --git a/libs/smart-contracts/src/utils/hexadecimalify.test.ts b/libs/smart-contracts/src/utils/hexadecimalify.test.ts new file mode 100644 index 000000000..1750e2d7d --- /dev/null +++ b/libs/smart-contracts/src/utils/hexadecimalify.test.ts @@ -0,0 +1,6 @@ +import { hexadecimalify } from './hexadecimalify'; + +test('Prepends strings with 0x', () => { + expect(hexadecimalify('abc')).toEqual('0xabc'); + expect(hexadecimalify('123456789')).toEqual('0x123456789'); +}); diff --git a/libs/smart-contracts/src/utils/hexadecimalify.ts b/libs/smart-contracts/src/utils/hexadecimalify.ts new file mode 100644 index 000000000..6f4cb3264 --- /dev/null +++ b/libs/smart-contracts/src/utils/hexadecimalify.ts @@ -0,0 +1,3 @@ +export function hexadecimalify(str: string) { + return `0x${str}`; +} diff --git a/libs/smart-contracts/src/utils/index.ts b/libs/smart-contracts/src/utils/index.ts new file mode 100644 index 000000000..967db1f40 --- /dev/null +++ b/libs/smart-contracts/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './decimals'; +export * from './ascii-to-hex'; +export * from './hexadecimalify'; diff --git a/libs/smart-contracts/tsconfig.json b/libs/smart-contracts/tsconfig.json new file mode 100644 index 000000000..d019eb341 --- /dev/null +++ b/libs/smart-contracts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "esModuleInterop": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/smart-contracts/tsconfig.lib.json b/libs/smart-contracts/tsconfig.lib.json new file mode 100644 index 000000000..18b626731 --- /dev/null +++ b/libs/smart-contracts/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": [] + }, + "include": ["**/*.ts"], + "exclude": ["**/*.spec.ts", "**/*.test.ts"] +} diff --git a/libs/smart-contracts/tsconfig.spec.json b/libs/smart-contracts/tsconfig.spec.json new file mode 100644 index 000000000..a18afb604 --- /dev/null +++ b/libs/smart-contracts/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 70ee1ff69..3537ead87 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -26,6 +26,7 @@ "@vegaprotocol/order-list": ["libs/order-list/src/index.ts"], "@vegaprotocol/positions": ["libs/positions/src/index.ts"], "@vegaprotocol/react-helpers": ["libs/react-helpers/src/index.ts"], + "@vegaprotocol/smart-contracts": ["libs/smart-contracts/src/index.ts"], "@vegaprotocol/tailwindcss-config": [ "libs/tailwindcss-config/src/index.js" ], diff --git a/workspace.json b/workspace.json index 96feca1c0..2aa1f9a70 100644 --- a/workspace.json +++ b/workspace.json @@ -16,6 +16,7 @@ "react-helpers": "libs/react-helpers", "simple-trading-app": "apps/simple-trading-app", "simple-trading-app-e2e": "apps/simple-trading-app-e2e", + "smart-contracts": "libs/smart-contracts", "static": "apps/static", "stats": "apps/stats", "stats-e2e": "apps/stats-e2e",