diff --git a/user-verifier/tests/artifacts/ProxyAdmin.json b/artifacts/ProxyAdmin.json similarity index 100% rename from user-verifier/tests/artifacts/ProxyAdmin.json rename to artifacts/ProxyAdmin.json diff --git a/artifacts/RewardDistributionContract.json b/artifacts/RewardDistributionContract.json new file mode 100644 index 0000000..67120b1 --- /dev/null +++ b/artifacts/RewardDistributionContract.json @@ -0,0 +1,494 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "RewardDistributionContract", + "sourceName": "contracts/airdao-reward-distribution/Reward-Distribution.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AccessControlBadConfirmation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "neededRole", + "type": "bytes32" + } + ], + "name": "AccessControlUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ClaimReward", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "name": "RevertReward", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "grantor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "eventName", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "region", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "community", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "pseudo", + "type": "string" + } + ], + "name": "Reward", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MODERATOR_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "claimRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "wallets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "string", + "name": "eventName", + "type": "string" + }, + { + "internalType": "string", + "name": "region", + "type": "string" + }, + { + "internalType": "string", + "name": "community", + "type": "string" + }, + { + "internalType": "string", + "name": "pseudo", + "type": "string" + } + ], + "name": "distributeRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callerConfirmation", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "revertRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610022640100000000610027810204565b61010c565b600061003a6401000000006100e8810204565b805490915068010000000000000000900460ff1615610085576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80546001604060020a03908116146100e557805467ffffffffffffffff19166001604060020a0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b611a08806200011c6000396000f3fe6080604052600436106100fe576000357c0100000000000000000000000000000000000000000000000000000000900480638129fc1c1161009c578063a217fddf11610076578063a217fddf1461031e578063b7fe450314610333578063ca15c87314610353578063d547741f1461037357600080fd5b80638129fc1c146102525780639010d07c1461026757806391d14854146102ac57600080fd5b80632f2ff15d116100d85780632f2ff15d146101be57806336568abe146101de578063666d2b49146101fe578063797669c91461021e57600080fd5b806301ffc9a71461010a5780630962ef791461013f578063248a9ca31461016157600080fd5b3661010557005b600080fd5b34801561011657600080fd5b5061012a6101253660046114b9565b610393565b60405190151581526020015b60405180910390f35b34801561014b57600080fd5b5061015f61015a3660046114fb565b6103ef565b005b34801561016d57600080fd5b506101b061017c3660046114fb565b60009081527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604090206001015490565b604051908152602001610136565b3480156101ca57600080fd5b5061015f6101d936600461153d565b6105c6565b3480156101ea57600080fd5b5061015f6101f936600461153d565b610610565b34801561020a57600080fd5b5061015f610219366004611724565b610669565b34801561022a57600080fd5b506101b07f71f3d55856e4058ed06ee057d79ada615f65cdf5f9ee88181b914225088f834f81565b34801561025e57600080fd5b5061015f610ad0565b34801561027357600080fd5b50610287610282366004611819565b610c52565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610136565b3480156102b857600080fd5b5061012a6102c736600461153d565b60009182527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b34801561032a57600080fd5b506101b0600081565b34801561033f57600080fd5b5061015f61034e3660046114fb565b610c93565b34801561035f57600080fd5b506101b061036e3660046114fb565b610e8b565b34801561037f57600080fd5b5061015f61038e36600461153d565b610eca565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f5a05180f0000000000000000000000000000000000000000000000000000000014806103e957506103e982610f0e565b92915050565b600081815260208190526040902060010154819015801590610427575060008181526020818152604080832033845290915290205415155b610492576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f52657761726473206e6f7420617661696c61626c65000000000000000000000060448201526064015b60405180910390fd5b60008281526020818152604080832033845290915290205480156105c157604051339082156108fc029083906000818181858888f193505050501580156104dd573d6000803e3d6000fd5b5060008381526020818152604080832033845280835290832083905585835290829052600101805483929061051390849061186a565b92505081905550806001600082825461052c919061186a565b90915550506000838152602081905260408120600101549003610589576000838152602081905260408120600181019190915560020180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b604051818152839033907fe74e5c9d4ac1fc33412485f18c159a0a391efe287ab3fd271123f30e6bacf4e39060200160405180910390a35b505050565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604090206001015461060081610fa5565b61060a8383610fb2565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8116331461065f576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105c18282611009565b3360009081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff16610701576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4e6f7420617574686f72697a65640000000000000000000000000000000000006044820152606401610489565b60008651118015610713575084518651145b610779576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f496e76616c696420696e707574000000000000000000000000000000000000006044820152606401610489565b6002845110156107e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e76616c6964206576656e744e616d650000000000000000000000000000006044820152606401610489565b600283511015610851576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f496e76616c696420726567696f6e0000000000000000000000000000000000006044820152606401610489565b6000805b87518110156109f65760008882815181106108725761087261187d565b6020026020010151905060008883815181106108905761089061187d565b602090810291909101810151436000908152808352604080822073ffffffffffffffffffffffffffffffffffffffff87168352909352919091205490915015610935576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f52657761726420206475706c69636174696f6e000000000000000000000000006044820152606401610489565b8173ffffffffffffffffffffffffffffffffffffffff163273ffffffffffffffffffffffffffffffffffffffff167f147ec819427c7eea1ef372153c638ba290750e353359f336e46cb5aa5672eeac83428c8c8c8c60405161099c969594939291906118f2565b60405180910390a34360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915290208190556109df8185611958565b9350505080806109ee9061196b565b915050610855565b50600154610a0590303161186a565b811115610a6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f496e76616c696420616d6f756e7420666f7220726577617264730000000000006044820152606401610489565b4360009081526020819052604081206001808201849055600290910180547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790558054839290610ac2908490611958565b909155505050505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610b1b5750825b905060008267ffffffffffffffff166001148015610b385750303b155b905081158015610b46575080155b15610b7d576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610bde5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610be9600033610fb2565b508315610c4b5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050565b60008281527fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000602081905260408220610c8b9084611057565b949350505050565b3360009081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff16610d2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4e6f7420617574686f72697a65640000000000000000000000000000000000006044820152606401610489565b6000818152602081905260409020600101548015610e875760008281526020819052604090206002015473ffffffffffffffffffffffffffffffffffffffff16338114610dfa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f6e6c7920696e697469616c206469737472696275746f722063616e2072657660448201527f6f6b6520756e636c61696d6564207265776172647300000000000000000000006064820152608401610489565b60008381526020819052604081206001808201839055600290910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690558054849290610e4b90849061186a565b909155505060405182815283907f57cad6594079128e9eb270d65f3983f2c822bbf52fbab455a09c02de19a262229060200160405180910390a2505b5050565b60008181527fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000602081905260408220610ec390611063565b9392505050565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040902060010154610f0481610fa5565b61060a8383611009565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806103e957507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146103e9565b610faf813361106d565b50565b60007fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e8237170593200081610fe08585611114565b90508015610c8b5760008581526020839052604090206110009085611235565b50949350505050565b60007fc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000816110378585611257565b90508015610c8b5760008581526020839052604090206110009085611335565b6000610ec38383611357565b60006103e9825490565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610e87576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610489565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff1661122b5760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556111c73390565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506103e9565b60009150506103e9565b6000610ec38373ffffffffffffffffffffffffffffffffffffffff8416611381565b60008281527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290915282205460ff161561122b5760008481526020828152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506103e9565b6000610ec38373ffffffffffffffffffffffffffffffffffffffff84166113d0565b600082600001828154811061136e5761136e61187d565b9060005260206000200154905092915050565b60008181526001830160205260408120546113c8575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556103e9565b5060006103e9565b6000818152600183016020526040812054801561122b5760006113f460018361186a565b85549091506000906114089060019061186a565b905080821461146d5760008660000182815481106114285761142861187d565b906000526020600020015490508087600001848154811061144b5761144b61187d565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061147e5761147e6119a3565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506103e9565b6000602082840312156114cb57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610ec357600080fd5b60006020828403121561150d57600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461153857600080fd5b919050565b6000806040838503121561155057600080fd5b8235915061156060208401611514565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156115c1576115c1611569565b604052919050565b600067ffffffffffffffff8211156115e3576115e3611569565b5060209081020190565b600082601f8301126115fe57600080fd5b8135602061161361160e836115c9565b611598565b8281529181028401810191818101908684111561162f57600080fd5b8286015b848110156116515761164481611514565b8352918301918301611633565b509695505050505050565b600082601f83011261166d57600080fd5b8135602061167d61160e836115c9565b8281529181028401810191818101908684111561169957600080fd5b8286015b84811015611651578035835291830191830161169d565b600082601f8301126116c557600080fd5b813567ffffffffffffffff8111156116df576116df611569565b6116f26020601f19601f84011601611598565b81815284602083860101111561170757600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c0878903121561173d57600080fd5b863567ffffffffffffffff8082111561175557600080fd5b6117618a838b016115ed565b9750602089013591508082111561177757600080fd5b6117838a838b0161165c565b9650604089013591508082111561179957600080fd5b6117a58a838b016116b4565b955060608901359150808211156117bb57600080fd5b6117c78a838b016116b4565b945060808901359150808211156117dd57600080fd5b6117e98a838b016116b4565b935060a08901359150808211156117ff57600080fd5b5061180c89828a016116b4565b9150509295509295509295565b6000806040838503121561182c57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156103e9576103e961183b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000815180845260005b818110156118d2576020818501810151868301820152016118b6565b506000602082860101526020601f19601f83011685010191505092915050565b86815285602082015260c06040820152600061191160c08301876118ac565b828103606084015261192381876118ac565b9050828103608084015261193781866118ac565b905082810360a084015261194b81856118ac565b9998505050505050505050565b808201808211156103e9576103e961183b565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361199c5761199c61183b565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220e472df6b89f46bb20a5342aefc4ae48059b58d3ed8181f6cf39044f1387408f964736f6c63430008140033", + "deployedBytecode": "", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/user-verifier/tests/artifacts/TransparentUpgradeableProxy.json b/artifacts/TransparentUpgradeableProxy.json similarity index 100% rename from user-verifier/tests/artifacts/TransparentUpgradeableProxy.json rename to artifacts/TransparentUpgradeableProxy.json diff --git a/event-indexer/Cargo.toml b/event-indexer/Cargo.toml index 34e1a58..e55d1ed 100644 --- a/event-indexer/Cargo.toml +++ b/event-indexer/Cargo.toml @@ -5,6 +5,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "event_indexer" +path = "src/lib.rs" + +[[bin]] +name = "airdao-event-indexer" +path = "src/main.rs" + [dependencies] # Local shared = { path = "../shared/" } @@ -66,5 +74,5 @@ clap = { workspace = true } assert_matches = { workspace = true } [features] -default = [] -enable-integration-tests = [] \ No newline at end of file +default = ["enable-integration-tests"] +enable-integration-tests = ["shared/enable-integration-tests"] \ No newline at end of file diff --git a/event-indexer/src/event_listener.rs b/event-indexer/src/event_listener.rs index fabd638..3dfaaf6 100644 --- a/event-indexer/src/event_listener.rs +++ b/event-indexer/src/event_listener.rs @@ -12,8 +12,6 @@ use futures_util::StreamExt; use std::{collections::HashMap, sync::Arc}; use tokio::sync::mpsc; -use crate::config::AppConfig; - const ERR_UNHANDLED_EVENT: &str = "Unhandled event"; const HUMAN_SBT_MINT_EVENT_SIGNATURE: ethereum_types::H256 = shared::event_signature!("SBTMint(address,uint256,uint256)"); @@ -21,23 +19,21 @@ const NON_EXP_SBT_MINT_EVENT_SIGNATURE: ethereum_types::H256 = shared::event_signature!("SBTMint(address)"); const SBT_BURN_EVENT_SIGNATURE: ethereum_types::H256 = shared::event_signature!("SBTBurn(address)"); const REWARD_EVENT_SIGNATURE: ethereum_types::H256 = - shared::event_signature!("Reward(address,amount,timestamp)"); + shared::event_signature!("Reward(address,address,uint256,uint256,string,string,string,string)"); const CLAIM_REWARD_EVENT_SIGNATURE: ethereum_types::H256 = - shared::event_signature!("ClaimReward(address,id,amount)"); + shared::event_signature!("ClaimReward(address,uint256,uint256)"); const REVERT_REWARD_EVENT_SIGNATURE: ethereum_types::H256 = - shared::event_signature!("RevertReward(id,total)"); + shared::event_signature!("RevertReward(uint256,uint256)"); pub struct EventListener { provider: Arc>, block_number: u64, sbt_name_by_addr: HashMap, - config: Arc, + contracts: HashMap, } #[derive(Clone)] struct EventLoopContext { - #[allow(unused)] - config: Arc, tx: mpsc::UnboundedSender, block: u64, } @@ -134,10 +130,17 @@ impl std::fmt::Display for SBTBurnEvent { #[derive(Debug, Clone, EthEvent)] #[ethevent(name = "Reward")] pub struct RewardEvent { + #[ethevent(indexed)] + pub grantor: Address, #[ethevent(indexed)] pub wallet: Address, pub amount: U256, pub timestamp: U256, + #[ethevent(name = "eventName")] + pub name: String, + pub region: String, + pub community: String, + pub pseudo: String, } impl std::fmt::Display for RewardEvent { @@ -180,7 +183,7 @@ impl std::fmt::Display for RevertRewardEvent { pub enum GovEvent { SBTMint(Address), SBTBurn(Address), - Reward(Address, U256, U256), + Reward(RewardEvent), ClaimReward(Address, u64), RevertReward(u64), } @@ -234,8 +237,7 @@ impl EthEvent for GovEvent { // Reward distribution events Some(topic) if topic.eq(&REWARD_EVENT_SIGNATURE) => { - ::decode_log(log) - .map(|event| Self::Reward(event.wallet, event.amount, event.timestamp)) + ::decode_log(log).map(Self::Reward) } Some(topic) if topic.eq(&CLAIM_REWARD_EVENT_SIGNATURE) => { ::decode_log(log) @@ -265,19 +267,15 @@ impl EthEvent for GovEvent { impl EventListener { pub fn new( - config: AppConfig, + contracts: HashMap, provider: Arc>, block_number: u64, ) -> anyhow::Result { Ok(Self { provider, block_number, - sbt_name_by_addr: config - .contracts - .iter() - .map(|(k, v)| (*v, k.clone())) - .collect(), - config: Arc::new(config), + sbt_name_by_addr: contracts.iter().map(|(k, v)| (*v, k.clone())).collect(), + contracts, }) } @@ -286,7 +284,6 @@ impl EventListener { tx: mpsc::UnboundedSender, ) -> anyhow::Result<()> { let mut context = EventLoopContext { - config: self.config.clone(), tx, block: self.block_number, }; @@ -306,7 +303,7 @@ impl EventListener { }; let mut event: Event>, Provider, _> = EthEvent::new( - Filter::new().address(self.config.contracts.values().copied().collect::>()), + Filter::new().address(self.contracts.values().copied().collect::>()), self.provider.clone(), ) .from_block(context.block); diff --git a/event-indexer/src/lib.rs b/event-indexer/src/lib.rs new file mode 100644 index 0000000..8ad2a28 --- /dev/null +++ b/event-indexer/src/lib.rs @@ -0,0 +1,6 @@ +#[allow(unused)] +mod auto_refresh_token; +pub mod config; +pub mod event_listener; +#[allow(unused)] +mod gov_db_provider; diff --git a/event-indexer/src/main.rs b/event-indexer/src/main.rs index 075a893..1f33df1 100644 --- a/event-indexer/src/main.rs +++ b/event-indexer/src/main.rs @@ -37,7 +37,11 @@ async fn main() -> Result<(), Box> { let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::(); - let listener = EventListener::new(config, provider, indexer_state_redis_cache.block_number)?; + let listener = EventListener::new( + config.contracts, + provider, + indexer_state_redis_cache.block_number, + )?; tokio::spawn(async move { let idle_interval = std::time::Duration::from_secs(10); @@ -66,7 +70,7 @@ async fn main() -> Result<(), Box> { block_number = event_notification.block_number ); - let result = match event_notification.event { + let result = match &event_notification.event { GovEvent::SBTMint(wallet) => { let sbt = SBTInfo { name: event_notification.contract_name.clone(), @@ -74,26 +78,33 @@ async fn main() -> Result<(), Box> { issued_at_block: event_notification.block_number, }; - gov_db_provider.upsert_user_sbt(wallet, sbt).await + gov_db_provider.upsert_user_sbt(*wallet, sbt).await } GovEvent::SBTBurn(wallet) => { gov_db_provider - .remove_user_sbt(wallet, event_notification.contract_address) + .remove_user_sbt(*wallet, event_notification.contract_address) .await } - GovEvent::Reward(wallet, amount, timestamp) => { + GovEvent::Reward(event) => { gov_db_provider .insert_reward(RewardInfo { - wallet, id: event_notification.block_number, - amount, - timestamp: timestamp.as_u64(), + grantor: event.grantor, + wallet: event.wallet, + amount: event.amount, + timestamp: event.timestamp.as_u64(), + event_name: event.name.clone(), + region: event.region.clone(), + community: Some(event.community.clone()).filter(|v| !v.is_empty()), + pseudo: Some(event.pseudo.clone()).filter(|v| !v.is_empty()), status: RewardStatus::Granted, }) .await } - GovEvent::ClaimReward(wallet, id) => gov_db_provider.claim_reward(wallet, id).await, - GovEvent::RevertReward(id) => gov_db_provider.revert_reward(id).await, + GovEvent::ClaimReward(wallet, id) => { + gov_db_provider.claim_reward(*wallet, *id).await + } + GovEvent::RevertReward(id) => gov_db_provider.revert_reward(*id).await, }; if let Ok(axum::Json(())) = result { diff --git a/event-indexer/tests/test_rewards_contract.rs b/event-indexer/tests/test_rewards_contract.rs new file mode 100644 index 0000000..7959cce --- /dev/null +++ b/event-indexer/tests/test_rewards_contract.rs @@ -0,0 +1,233 @@ +#![cfg(feature = "enable-integration-tests")] +use assert_matches::assert_matches; +use ethereum_types::U64; +use ethers::utils::parse_ether; +use ethers_providers::{Provider, Ws}; +use std::{str::FromStr, sync::Arc}; +use web3::{ + signing::Key, + types::{BlockNumber, TransactionRequest}, +}; + +use shared::tests::*; + +use event_indexer::event_listener::{EventListener, GovEvent, GovEventNotification}; + +#[tokio::test] +async fn test_rewards_contract() -> Result<(), anyhow::Error> { + // Account #0 private key from Hardhat local node + let owner_secret = web3::signing::SecretKey::from_str( + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + )?; + + let owner = web3::signing::SecretKeyRef::from(&owner_secret); + + let wallets_secret = [ + // Account #16 private key from Hardhat local node + web3::signing::SecretKey::from_str( + "ea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0", + )?, + // Account #17 private key from Hardhat local node + web3::signing::SecretKey::from_str( + "689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd", + )?, + // Account #18 private key from Hardhat local node + web3::signing::SecretKey::from_str( + "de9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0", + )?, + // Account #19 private key from Hardhat local node + web3::signing::SecretKey::from_str( + "df57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e", + )?, + ]; + + let wallets = wallets_secret + .iter() + .map(web3::signing::SecretKeyRef::from) + .collect::>(); + + // Default http url for Hardhat local node + let http = web3::transports::Http::new("http://127.0.0.1:8545")?; + + let web3_client = web3::Web3::new(http); + + let rewards_contract = deploy_upgradeable_contract( + web3_client.eth(), + include_str!("../../artifacts/RewardDistributionContract.json"), + (), + &owner_secret, + ) + .await?; + + let rewards_contract_addr = rewards_contract.address(); + + let _ = web3_client + .eth() + .send_transaction(TransactionRequest { + from: owner.address(), + to: Some(rewards_contract_addr), + gas: None, + gas_price: None, + value: Some(parse_ether("1")?), + data: None, + nonce: None, + condition: None, + ..Default::default() + }) + .await?; + + let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::(); + let start_block = web3_client.eth().block_number().await?.as_u64(); + + let listener_handle = tokio::spawn(async move { + let provider = Arc::new(Provider::::connect("ws://127.0.0.1:8545").await?); + let listener = EventListener::new( + [( + "RewardDistributionContract".to_owned(), + rewards_contract_addr, + )] + .into_iter() + .collect(), + provider, + start_block, + )?; + + listener.start(tx).await + }); + + signed_call( + &rewards_contract, + "distributeRewards", + ( + wallets + .iter() + .map(|wallet| wallet.address()) + .collect::>(), + wallets + .iter() + .map(|_| ethereum_types::U256::one()) + .collect::>(), + "Rewards".to_owned(), + "NA".to_owned(), + "TestCommunity".to_owned(), + "".to_owned(), + ), + None, + &owner_secret, + ) + .await?; + + let mut events = Vec::with_capacity(wallets.len()); + let received = rx.recv_many(&mut events, wallets.len()).await; + let rewards_id = events.first().unwrap().block_number; + + assert_eq!(received, wallets.len()); + assert_matches!( + events + .into_iter() + .map(|notification| notification.event) + .collect::>() + .as_slice(), + &[ + GovEvent::Reward(_), + GovEvent::Reward(_), + GovEvent::Reward(_), + GovEvent::Reward(_), + ] + ); + + let tx_receipt = signed_call( + &rewards_contract, + "claimRewards", + rewards_id, + None, + &wallets_secret[0], + ) + .await?; + + assert_matches!( + rx.recv().await.map(|notification| notification.event), + Some(GovEvent::ClaimReward(wallet, id)) if wallet == wallets[0].address() && id == rewards_id + ); + + // Verify that rewards were acquired after claim + assert_eq!( + web3_client + .eth() + .balance( + wallets[0].address(), + tx_receipt + .block_number + .and_then(|block| block.checked_sub(U64::one())) + .map(BlockNumber::Number) + ) + .await? + .checked_sub(transaction_cost(&tx_receipt).unwrap_or_default()) + .and_then(|value| value.checked_add(ethereum_types::U256::one())) + .unwrap_or_default(), + web3_client + .eth() + .balance(wallets[0].address(), None) + .await? + ); + + // Verify that contract balance changes + assert_eq!( + web3_client + .eth() + .balance( + rewards_contract_addr, + tx_receipt + .block_number + .and_then(|block| block.checked_sub(U64::one())) + .map(BlockNumber::Number) + ) + .await? + .checked_sub(ethereum_types::U256::one()) + .unwrap_or_default(), + web3_client + .eth() + .balance(rewards_contract_addr, None) + .await? + ); + + // Double claim not possible + assert!(signed_call( + &rewards_contract, + "claimRewards", + rewards_id, + None, + &wallets_secret[0], + ) + .await + .is_err()); + + signed_call( + &rewards_contract, + "revertRewards", + rewards_id, + None, + &owner_secret, + ) + .await?; + + assert_matches!( + rx.recv().await.map(|notification| notification.event), + Some(GovEvent::RevertReward(id)) if id == rewards_id + ); + + // Unable to claim reverted rewards + assert!(signed_call( + &rewards_contract, + "claimRewards", + rewards_id, + None, + &wallets_secret[1], + ) + .await + .is_err()); + + listener_handle.abort(); + + Ok(()) +} diff --git a/gov-portal-db/Cargo.toml b/gov-portal-db/Cargo.toml index f313908..583f058 100644 --- a/gov-portal-db/Cargo.toml +++ b/gov-portal-db/Cargo.toml @@ -63,5 +63,5 @@ clap = { workspace = true } assert_matches = { workspace = true } [features] -default = [] -enable-integration-tests = [] \ No newline at end of file +default = ["enable-integration-tests"] +enable-integration-tests = ["shared/enable-integration-tests"] \ No newline at end of file diff --git a/gov-portal-db/tests/test_rewards_manager.rs b/gov-portal-db/tests/test_rewards_manager.rs index 5f35db5..d82d144 100644 --- a/gov-portal-db/tests/test_rewards_manager.rs +++ b/gov-portal-db/tests/test_rewards_manager.rs @@ -37,31 +37,42 @@ async fn test_update_reward() -> Result<(), anyhow::Error> { let addr_1 = Address::from_low_u64_le(1); let addr_2 = Address::from_low_u64_le(2); + let addr_grantor = Address::from_low_u64_le(11111111); rewards_manager .update_reward(UpdateRewardKind::Grant(RewardInfo { - wallet: addr_1, id: 1, + grantor: addr_grantor, + wallet: addr_1, amount: U256::one(), timestamp: Utc::now().timestamp() as u64, + event_name: "Rewards".to_owned(), + region: "NA".to_owned(), + community: None, + pseudo: None, status: RewardStatus::Granted, })) .await?; rewards_manager .update_reward(UpdateRewardKind::Grant(RewardInfo { - wallet: addr_2, id: 2, + grantor: addr_grantor, + wallet: addr_2, amount: U256::one(), timestamp: Utc::now().timestamp() as u64, + event_name: "Rewards".to_owned(), + region: "NA".to_owned(), + community: None, + pseudo: None, status: RewardStatus::Granted, })) .await?; rewards_manager .update_reward(UpdateRewardKind::Claim { - wallet: addr_1, id: 1, + wallet: addr_1, }) .await?; @@ -107,6 +118,7 @@ async fn test_rewards_endpoint() -> Result<(), anyhow::Error> { .await?; let now = Utc::now().timestamp() as u64; + let addr_grantor = Address::from_low_u64_le(11111111); futures_util::future::join_all((1u64..=29).map(|i| { let users_manager = rewards_manager.clone(); @@ -115,10 +127,15 @@ async fn test_rewards_endpoint() -> Result<(), anyhow::Error> { let wallet = Address::from_low_u64_le(i / 3 + 1); users_manager .update_reward(UpdateRewardKind::Grant(RewardInfo { - wallet, id: i % 10 + 1, + grantor: addr_grantor, + wallet, amount: U256::from(i * 1_000_000_000), timestamp: now + i % 10 + 1, + event_name: "Rewards".to_owned(), + region: "NA".to_owned(), + community: None, + pseudo: None, status: RewardStatus::Granted, })) .await @@ -181,16 +198,26 @@ async fn test_rewards_endpoint() -> Result<(), anyhow::Error> { vec![ RewardInfo { id: 2, + grantor: Address::from_low_u64_le(11111111), wallet: Address::from_low_u64_le(1), amount: U256::from(1_000_000_000), timestamp: now + 2, + event_name: "Rewards".to_owned(), + region: "NA".to_owned(), + community: None, + pseudo: None, status: RewardStatus::Granted, }, RewardInfo { id: 3, + grantor: Address::from_low_u64_le(11111111), wallet: Address::from_low_u64_le(1), amount: U256::from(2_000_000_000), timestamp: now + 3, + event_name: "Rewards".to_owned(), + region: "NA".to_owned(), + community: None, + pseudo: None, status: RewardStatus::Granted, } ] diff --git a/shared/Cargo.toml b/shared/Cargo.toml index e7c257b..f6c4ab9 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -46,3 +46,6 @@ hex = { workspace = true } [dev-dependencies] assert_matches = { workspace = true } uuid = { workspace = true } + +[features] +enable-integration-tests = [] diff --git a/shared/src/common.rs b/shared/src/common.rs index 63487f5..6cf44ef 100644 --- a/shared/src/common.rs +++ b/shared/src/common.rs @@ -132,10 +132,17 @@ pub struct Rewards { #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct RewardInfo { - pub wallet: Address, pub id: u64, + pub grantor: Address, + pub wallet: Address, pub amount: U256, pub timestamp: u64, + pub event_name: String, + pub region: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub community: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pseudo: Option, #[serde(default, skip_serializing_if = "RewardStatus::is_granted")] pub status: RewardStatus, } @@ -162,8 +169,15 @@ pub struct RewardsDbEntry { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RewardDbEntry { + pub grantor: Address, pub amount: U256, pub timestamp: u64, + pub event_name: String, + pub region: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub community: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pseudo: Option, #[serde(default, skip_serializing_if = "RewardStatus::is_granted")] pub status: RewardStatus, } @@ -840,9 +854,14 @@ impl From<(u64, Address, RewardDbEntry)> for RewardInfo { fn from((id, wallet, value): (u64, Address, RewardDbEntry)) -> Self { Self { id, + grantor: value.grantor, wallet, amount: value.amount, timestamp: value.timestamp, + event_name: value.event_name, + region: value.region, + community: value.community, + pseudo: value.pseudo, status: value.status, } } @@ -854,8 +873,13 @@ impl From for (u64, Address, RewardDbEntry) { value.id, value.wallet, RewardDbEntry { + grantor: value.grantor, amount: value.amount, timestamp: value.timestamp, + event_name: value.event_name, + region: value.region, + community: value.community, + pseudo: value.pseudo, status: value.status, }, ) diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 5015d01..f7f884e 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -4,3 +4,6 @@ pub mod rpc_node_client; pub mod utils; pub use event_signature_macro::event_signature; + +#[cfg(any(test, feature = "enable-integration-tests"))] +pub mod tests; diff --git a/user-verifier/src/tests/common.rs b/shared/src/tests/common.rs similarity index 94% rename from user-verifier/src/tests/common.rs rename to shared/src/tests/common.rs index 4ea9f27..db69120 100644 --- a/user-verifier/src/tests/common.rs +++ b/shared/src/tests/common.rs @@ -61,7 +61,7 @@ pub async fn deploy_upgradeable_contract( let proxy_admin_contract = deploy_contract( eth.clone(), - include_str!("../../tests/artifacts/ProxyAdmin.json"), + include_str!("../../../artifacts/ProxyAdmin.json"), caller.address(), caller_secret, ) @@ -74,7 +74,7 @@ pub async fn deploy_upgradeable_contract( let proxy_contract = deploy_contract( eth.clone(), - include_str!("../../tests/artifacts/TransparentUpgradeableProxy.json"), + include_str!("../../../artifacts/TransparentUpgradeableProxy.json"), ( impl_contract.address(), proxy_admin_contract.address(), @@ -108,6 +108,9 @@ pub async fn deploy_contract( Contract::deploy(eth, abi.as_bytes())? .confirmations(0) + // .options(Options { + // ..Default::default() + // }) .execute(bytecode.to_string(), params, caller.address()) .await .map_err(anyhow::Error::from) @@ -219,3 +222,9 @@ pub async fn hardhat_set_coinbase( .await .map_err(anyhow::Error::from) } + +pub fn transaction_cost(receipt: &TransactionReceipt) -> Option { + receipt + .effective_gas_price + .and_then(|gas_price| gas_price.checked_mul(receipt.cumulative_gas_used)) +} diff --git a/user-verifier/src/tests/mod.rs b/shared/src/tests/mod.rs similarity index 100% rename from user-verifier/src/tests/mod.rs rename to shared/src/tests/mod.rs diff --git a/user-verifier/Cargo.toml b/user-verifier/Cargo.toml index 5d9b0bf..062eed7 100644 --- a/user-verifier/Cargo.toml +++ b/user-verifier/Cargo.toml @@ -52,4 +52,5 @@ rand = { workspace = true } assert_matches = { workspace = true } [features] -enable-integration-tests = ["jsonrpc-core"] \ No newline at end of file +default = ["enable-integration-tests"] +enable-integration-tests = ["jsonrpc-core", "shared/enable-integration-tests"] diff --git a/user-verifier/src/lib.rs b/user-verifier/src/lib.rs index 8085081..9ba0fde 100644 --- a/user-verifier/src/lib.rs +++ b/user-verifier/src/lib.rs @@ -4,6 +4,3 @@ pub mod explorer_client; pub mod server_nodes_manager; pub mod signer; pub mod validators_manager; - -#[cfg(any(test, feature = "enable-integration-tests"))] -pub mod tests; diff --git a/user-verifier/tests/test_sbt.rs b/user-verifier/tests/test_sbt.rs index 21be92b..9edf394 100644 --- a/user-verifier/tests/test_sbt.rs +++ b/user-verifier/tests/test_sbt.rs @@ -11,7 +11,8 @@ use web3::{ types::TransactionRequest, }; -use airdao_gov_user_verifier::{signer, tests::*}; +use airdao_gov_user_verifier::signer; +use shared::tests::*; const ERR_SBT_EXPIRED_OR_NOT_EXIST: &str = "Error: VM Exception while processing transaction: reverted with reason string 'SBT expired or not exist'"; const ERR_SBT_ALREADY_EXIST: &str = "Error: VM Exception while processing transaction: reverted with reason string 'This kind of SBT already exist'"; diff --git a/user-verifier/tests/test_server_nodes_manager.rs b/user-verifier/tests/test_server_nodes_manager.rs index 353be66..a131a04 100644 --- a/user-verifier/tests/test_server_nodes_manager.rs +++ b/user-verifier/tests/test_server_nodes_manager.rs @@ -2,11 +2,13 @@ use std::str::FromStr; use web3::signing::Key; -use airdao_gov_user_verifier::{ - server_nodes_manager::{ServerNodesManager, ServerNodesManagerConfig}, +use airdao_gov_user_verifier::server_nodes_manager::{ + ServerNodesManager, ServerNodesManagerConfig, +}; +use shared::{ + rpc_node_client::{RpcNodeClient, RpcNodeConfig}, tests::*, }; -use shared::rpc_node_client::{RpcNodeClient, RpcNodeConfig}; // const ERR_NODE_ALREADY_REGISTERED: &str = "Error: VM Exception while processing transaction: reverted with reason string 'node already registered'";