Extract entire permissioned balances logic to separate file
This commit is contained in:
parent
105703e2eb
commit
b727f1e14b
|
@ -8,7 +8,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
|
@ -2185,269 +2184,6 @@ func (m *Manager) CheckPermissionToJoin(id []byte, addresses []gethcommon.Addres
|
|||
|
||||
}
|
||||
|
||||
type PermissionedBalance struct {
|
||||
Type protobuf.CommunityTokenType `json:"type"`
|
||||
Symbol string `json:"symbol"`
|
||||
Name string `json:"name"`
|
||||
Amount *bigint.BigInt `json:"amount"`
|
||||
Decimals uint64 `json:"decimals"`
|
||||
}
|
||||
|
||||
func calculatePermissionedBalancesERC20(
|
||||
accountAddresses []gethcommon.Address,
|
||||
balances BalancesByChain,
|
||||
tokenPermissions []*CommunityTokenPermission,
|
||||
) map[gethcommon.Address]map[string]*PermissionedBalance {
|
||||
res := make(map[gethcommon.Address]map[string]*PermissionedBalance)
|
||||
|
||||
// Set with composite key (chain ID + wallet address + contract address) to
|
||||
// store if we already processed the balance.
|
||||
usedBalances := make(map[string]bool)
|
||||
|
||||
for _, permission := range tokenPermissions {
|
||||
for _, criteria := range permission.TokenCriteria {
|
||||
if criteria.Type != protobuf.CommunityTokenType_ERC20 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, accountAddress := range accountAddresses {
|
||||
for chainID, hexContractAddress := range criteria.ContractAddresses {
|
||||
usedKey := strconv.FormatUint(chainID, 10) + "-" + accountAddress.Hex() + "-" + hexContractAddress
|
||||
|
||||
if _, ok := balances[chainID]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := balances[chainID][accountAddress]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
contractAddress := gethcommon.HexToAddress(hexContractAddress)
|
||||
value, ok := balances[chainID][accountAddress][contractAddress]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip the contract address if it has been used already in the sum.
|
||||
if _, ok := usedBalances[usedKey]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := res[accountAddress]; !ok {
|
||||
res[accountAddress] = make(map[string]*PermissionedBalance, 0)
|
||||
}
|
||||
if _, ok := res[accountAddress][criteria.Symbol]; !ok {
|
||||
res[accountAddress][criteria.Symbol] = &PermissionedBalance{
|
||||
Type: criteria.Type,
|
||||
Symbol: criteria.Symbol,
|
||||
Name: criteria.Name,
|
||||
Decimals: criteria.Decimals,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(0)},
|
||||
}
|
||||
}
|
||||
|
||||
res[accountAddress][criteria.Symbol].Amount.Add(
|
||||
res[accountAddress][criteria.Symbol].Amount.Int,
|
||||
value.ToInt(),
|
||||
)
|
||||
usedBalances[usedKey] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func isERC721CriteriaSatisfied(tokenBalances []thirdparty.TokenBalance, criteria *protobuf.TokenCriteria) bool {
|
||||
for _, tokenID := range criteria.TokenIds {
|
||||
tokenIDBigInt := new(big.Int).SetUint64(tokenID)
|
||||
for _, asset := range tokenBalances {
|
||||
if asset.TokenID.Cmp(tokenIDBigInt) == 0 && asset.Balance.Sign() > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func calculatePermissionedBalancesERC721(
|
||||
accountAddresses []gethcommon.Address,
|
||||
balances CollectiblesByChain,
|
||||
tokenPermissions []*CommunityTokenPermission,
|
||||
) map[gethcommon.Address]map[string]*PermissionedBalance {
|
||||
res := make(map[gethcommon.Address]map[string]*PermissionedBalance)
|
||||
|
||||
// Set with composite key (chain ID + wallet address + contract address) to
|
||||
// store if we already processed the balance.
|
||||
usedBalances := make(map[string]bool)
|
||||
|
||||
for _, permission := range tokenPermissions {
|
||||
for _, criteria := range permission.TokenCriteria {
|
||||
if criteria.Type != protobuf.CommunityTokenType_ERC721 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, accountAddress := range accountAddresses {
|
||||
for chainID, hexContractAddress := range criteria.ContractAddresses {
|
||||
usedKey := strconv.FormatUint(chainID, 10) + "-" + accountAddress.Hex() + "-" + hexContractAddress
|
||||
|
||||
if _, ok := balances[chainID]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := balances[chainID][accountAddress]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
contractAddress := gethcommon.HexToAddress(hexContractAddress)
|
||||
tokenBalances, ok := balances[chainID][accountAddress][contractAddress]
|
||||
if !ok || len(tokenBalances) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip the contract address if it has been used already in the sum.
|
||||
if _, ok := usedBalances[usedKey]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
usedBalances[usedKey] = true
|
||||
|
||||
if _, ok := res[accountAddress]; !ok {
|
||||
res[accountAddress] = make(map[string]*PermissionedBalance, 0)
|
||||
}
|
||||
if _, ok := res[accountAddress][criteria.Symbol]; !ok {
|
||||
res[accountAddress][criteria.Symbol] = &PermissionedBalance{
|
||||
Type: criteria.Type,
|
||||
Symbol: criteria.Symbol,
|
||||
Name: criteria.Name,
|
||||
Decimals: criteria.Decimals,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(0)},
|
||||
}
|
||||
}
|
||||
|
||||
if isERC721CriteriaSatisfied(tokenBalances, criteria) {
|
||||
// We don't care about summing balances, thus setting as 1 is
|
||||
// sufficient.
|
||||
res[accountAddress][criteria.Symbol].Amount = &bigint.BigInt{Int: big.NewInt(1)}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func calculatePermissionedBalances(
|
||||
chainIDs []uint64,
|
||||
accountAddresses []gethcommon.Address,
|
||||
erc20Balances BalancesByChain,
|
||||
erc721Balances CollectiblesByChain,
|
||||
tokenPermissions []*CommunityTokenPermission,
|
||||
) map[gethcommon.Address][]PermissionedBalance {
|
||||
res := make(map[gethcommon.Address][]PermissionedBalance, 0)
|
||||
|
||||
aggregatedERC721Balances := calculatePermissionedBalancesERC721(accountAddresses, erc721Balances, tokenPermissions)
|
||||
for accountAddress, tokens := range aggregatedERC721Balances {
|
||||
for _, permissionedToken := range tokens {
|
||||
if permissionedToken.Amount.Sign() > 0 {
|
||||
res[accountAddress] = append(res[accountAddress], *permissionedToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aggregatedERC20Balances := calculatePermissionedBalancesERC20(accountAddresses, erc20Balances, tokenPermissions)
|
||||
for accountAddress, tokens := range aggregatedERC20Balances {
|
||||
for _, permissionedToken := range tokens {
|
||||
if permissionedToken.Amount.Sign() > 0 {
|
||||
res[accountAddress] = append(res[accountAddress], *permissionedToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func keepRoleTokenPermissions(tokenPermissions map[string]*CommunityTokenPermission) []*CommunityTokenPermission {
|
||||
res := make([]*CommunityTokenPermission, 0)
|
||||
for _, p := range tokenPermissions {
|
||||
if p.Type == protobuf.CommunityTokenPermission_BECOME_MEMBER ||
|
||||
p.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN ||
|
||||
p.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER ||
|
||||
p.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER {
|
||||
res = append(res, p)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetPermissionedBalances returns balances indexed by account address.
|
||||
//
|
||||
// It assumes balances in different chains with the same symbol can be summed.
|
||||
// It also assumes the criteria's decimals field is the same across different
|
||||
// criteria when they refer to the same asset (by symbol).
|
||||
func (m *Manager) GetPermissionedBalances(
|
||||
ctx context.Context,
|
||||
communityID types.HexBytes,
|
||||
accountAddresses []gethcommon.Address,
|
||||
) (map[gethcommon.Address][]PermissionedBalance, error) {
|
||||
community, err := m.GetByID(communityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if community == nil {
|
||||
return nil, errors.Errorf("community does not exist ID='%s'", communityID)
|
||||
}
|
||||
|
||||
tokenPermissions := keepRoleTokenPermissions(community.TokenPermissions())
|
||||
|
||||
allChainIDs, err := m.tokenManager.GetAllChainIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountsAndChainIDs := combineAddressesAndChainIDs(accountAddresses, allChainIDs)
|
||||
|
||||
erc20TokenCriteriaByChain, erc721TokenCriteriaByChain, _ := ExtractTokenCriteria(tokenPermissions)
|
||||
|
||||
accounts := make([]gethcommon.Address, 0, len(accountsAndChainIDs))
|
||||
for _, accountAndChainIDs := range accountsAndChainIDs {
|
||||
accounts = append(accounts, accountAndChainIDs.Address)
|
||||
}
|
||||
|
||||
erc20ChainIDsSet := make(map[uint64]bool)
|
||||
erc20TokenAddresses := make([]gethcommon.Address, 0)
|
||||
for chainID, criterionByContractAddress := range erc20TokenCriteriaByChain {
|
||||
erc20ChainIDsSet[chainID] = true
|
||||
for contractAddress := range criterionByContractAddress {
|
||||
erc20TokenAddresses = append(erc20TokenAddresses, gethcommon.HexToAddress(contractAddress))
|
||||
}
|
||||
}
|
||||
|
||||
erc721ChainIDsSet := make(map[uint64]bool)
|
||||
for chainID := range erc721TokenCriteriaByChain {
|
||||
erc721ChainIDsSet[chainID] = true
|
||||
}
|
||||
|
||||
erc20ChainIDs := calculateChainIDsSet(accountsAndChainIDs, erc20ChainIDsSet)
|
||||
erc721ChainIDs := calculateChainIDsSet(accountsAndChainIDs, erc721ChainIDsSet)
|
||||
|
||||
erc20Balances, err := m.tokenManager.GetBalancesByChain(ctx, accounts, erc20TokenAddresses, erc20ChainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
erc721Balances := make(CollectiblesByChain)
|
||||
if len(erc721ChainIDs) > 0 {
|
||||
balances, err := m.GetOwnedERC721Tokens(accounts, erc721TokenCriteriaByChain, erc721ChainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
erc721Balances = balances
|
||||
}
|
||||
|
||||
return calculatePermissionedBalances(allChainIDs, accountAddresses, erc20Balances, erc721Balances, tokenPermissions), nil
|
||||
}
|
||||
|
||||
func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, accounts []*protobuf.RevealedAccount) (bool, protobuf.CommunityMember_Roles, error) {
|
||||
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(accounts)
|
||||
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
|
||||
|
|
|
@ -226,309 +226,6 @@ func (s *ManagerSuite) TestRetrieveTokens() {
|
|||
s.Require().False(resp.Satisfied)
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) Test_calculatePermissionedBalances() {
|
||||
var mainnetID uint64 = 1
|
||||
var arbitrumID uint64 = 42161
|
||||
var gnosisID uint64 = 100
|
||||
chainIDs := []uint64{mainnetID, arbitrumID}
|
||||
|
||||
mainnetSNTContractAddress := gethcommon.HexToAddress("0xC")
|
||||
mainnetETHContractAddress := gethcommon.HexToAddress("0xA")
|
||||
arbitrumETHContractAddress := gethcommon.HexToAddress("0xB")
|
||||
mainnetTMasterAddress := gethcommon.HexToAddress("0x123")
|
||||
mainnetOwnerAddress := gethcommon.HexToAddress("0x1234")
|
||||
|
||||
account1Address := gethcommon.HexToAddress("0x1")
|
||||
account2Address := gethcommon.HexToAddress("0x2")
|
||||
account3Address := gethcommon.HexToAddress("0x3")
|
||||
accountAddresses := []gethcommon.Address{account1Address, account2Address, account3Address}
|
||||
|
||||
erc20Balances := make(BalancesByChain)
|
||||
erc721Balances := make(CollectiblesByChain)
|
||||
|
||||
erc20Balances[mainnetID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[arbitrumID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[gnosisID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
erc721Balances[mainnetID] = make(map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress)
|
||||
|
||||
// Account 1 balances
|
||||
erc20Balances[mainnetID][account1Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[mainnetID][account1Address][mainnetETHContractAddress] = intToBig(10)
|
||||
erc20Balances[arbitrumID][account1Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[arbitrumID][account1Address][arbitrumETHContractAddress] = intToBig(25)
|
||||
|
||||
// Account 2 balances
|
||||
erc20Balances[mainnetID][account2Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[mainnetID][account2Address][mainnetSNTContractAddress] = intToBig(120)
|
||||
erc721Balances[mainnetID][account2Address] = make(thirdparty.TokenBalancesPerContractAddress)
|
||||
erc721Balances[mainnetID][account2Address][mainnetTMasterAddress] = []thirdparty.TokenBalance{
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(456),
|
||||
Balance: uintToDecBig(1),
|
||||
},
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(123),
|
||||
Balance: uintToDecBig(2),
|
||||
},
|
||||
}
|
||||
erc721Balances[mainnetID][account2Address][mainnetOwnerAddress] = []thirdparty.TokenBalance{
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(100),
|
||||
Balance: uintToDecBig(6),
|
||||
},
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(101),
|
||||
Balance: uintToDecBig(1),
|
||||
},
|
||||
}
|
||||
|
||||
erc20Balances[arbitrumID][account2Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[arbitrumID][account2Address][arbitrumETHContractAddress] = intToBig(2)
|
||||
|
||||
// Account 3 balances. This account is used to assert zeroed balances are
|
||||
// removed from the final response.
|
||||
erc20Balances[mainnetID][account3Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[mainnetID][account3Address][mainnetETHContractAddress] = intToBig(0)
|
||||
|
||||
// A balance that should be ignored because the list of wallet addresses don't
|
||||
// contain any wallet in the Gnosis chain.
|
||||
erc20Balances[gnosisID][gethcommon.HexToAddress("0xF")] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[gnosisID][gethcommon.HexToAddress("0xF")][gethcommon.HexToAddress("0x99")] = intToBig(5)
|
||||
|
||||
tokenPermissions := []*CommunityTokenPermission{
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: "1",
|
||||
TokenIds: []uint64{123, 456},
|
||||
ContractAddresses: map[uint64]string{mainnetID: mainnetTMasterAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "OWNTEST",
|
||||
Name: "Owner-Test",
|
||||
Amount: "5",
|
||||
// No account has a positive balance for these token IDs, so we
|
||||
// expect this collectible to not be present in the final result.
|
||||
TokenIds: []uint64{666},
|
||||
ContractAddresses: map[uint64]string{mainnetID: mainnetOwnerAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_ADMIN,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Amount: "20",
|
||||
Decimals: 18,
|
||||
ContractAddresses: map[uint64]string{
|
||||
arbitrumID: arbitrumETHContractAddress.Hex(),
|
||||
mainnetID: mainnetETHContractAddress.Hex(),
|
||||
},
|
||||
},
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Amount: "4",
|
||||
Decimals: 18,
|
||||
ContractAddresses: map[uint64]string{arbitrumID: arbitrumETHContractAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "SNT",
|
||||
Name: "Status",
|
||||
Amount: "1000",
|
||||
Decimals: 16,
|
||||
ContractAddresses: map[uint64]string{mainnetID: mainnetSNTContractAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
// Unknown permission should be ignored.
|
||||
Type: protobuf.CommunityTokenPermission_UNKNOWN_TOKEN_PERMISSION,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "DAI",
|
||||
Name: "Dai",
|
||||
Amount: "7",
|
||||
Decimals: 12,
|
||||
ContractAddresses: map[uint64]string{mainnetID: "0x1234567"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actual := calculatePermissionedBalances(
|
||||
chainIDs,
|
||||
accountAddresses,
|
||||
erc20Balances,
|
||||
erc721Balances,
|
||||
tokenPermissions,
|
||||
)
|
||||
|
||||
expected := make(map[gethcommon.Address][]PermissionedBalance)
|
||||
expected[account1Address] = []PermissionedBalance{
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Decimals: 18,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(35)},
|
||||
},
|
||||
}
|
||||
expected[account2Address] = []PermissionedBalance{
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Decimals: 18,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(2)},
|
||||
},
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "SNT",
|
||||
Name: "Status",
|
||||
Decimals: 16,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(120)},
|
||||
},
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(1)},
|
||||
},
|
||||
}
|
||||
|
||||
_, ok := actual[account1Address]
|
||||
s.Require().True(ok, "not found account1Address='%s'", account1Address)
|
||||
_, ok = actual[account1Address]
|
||||
s.Require().True(ok, "not found account2Address='%s'", account2Address)
|
||||
|
||||
for accountAddress, permissionedTokens := range actual {
|
||||
s.Require().ElementsMatch(expected[accountAddress], permissionedTokens, "accountAddress='%s'", accountAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) Test_GetPermissionedBalances() {
|
||||
m, collectiblesManager, tokenManager := s.setupManagerForTokenPermissions()
|
||||
s.Require().NotNil(m)
|
||||
s.Require().NotNil(collectiblesManager)
|
||||
|
||||
request := &requests.CreateCommunity{
|
||||
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
||||
}
|
||||
community, err := m.CreateCommunity(request, true)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(community)
|
||||
|
||||
accountAddress := gethcommon.HexToAddress("0x1")
|
||||
accountAddresses := []gethcommon.Address{accountAddress}
|
||||
|
||||
var chainID uint64 = 5
|
||||
erc20ETHAddress := gethcommon.HexToAddress("0xA")
|
||||
erc721Address := gethcommon.HexToAddress("0x123")
|
||||
|
||||
permissionRequest := &requests.CreateCommunityTokenPermission{
|
||||
CommunityID: community.ID(),
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Amount: "3",
|
||||
Decimals: 18,
|
||||
ContractAddresses: map[uint64]string{chainID: erc20ETHAddress.Hex()},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, changes, err := m.CreateCommunityTokenPermission(permissionRequest)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(changes.TokenPermissionsAdded, 1)
|
||||
|
||||
permissionRequest = &requests.CreateCommunityTokenPermission{
|
||||
CommunityID: community.ID(),
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: "1",
|
||||
TokenIds: []uint64{666},
|
||||
ContractAddresses: map[uint64]string{chainID: erc721Address.Hex()},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, changes, err = m.CreateCommunityTokenPermission(permissionRequest)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(changes.TokenPermissionsAdded, 1)
|
||||
|
||||
tokenManager.setResponse(chainID, accountAddress, erc20ETHAddress, 42)
|
||||
collectiblesManager.setResponse(chainID, accountAddress, erc721Address, []thirdparty.TokenBalance{
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(666),
|
||||
Balance: uintToDecBig(15),
|
||||
},
|
||||
})
|
||||
|
||||
actual, err := m.GetPermissionedBalances(context.Background(), community.ID(), accountAddresses)
|
||||
s.Require().NoError(err)
|
||||
|
||||
expected := make(map[gethcommon.Address][]PermissionedBalance)
|
||||
expected[accountAddress] = []PermissionedBalance{
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Decimals: 18,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(42)},
|
||||
},
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(1)},
|
||||
},
|
||||
}
|
||||
|
||||
_, ok := actual[accountAddress]
|
||||
s.Require().True(ok, "not found accountAddress='%s'", accountAddress)
|
||||
|
||||
for address, permissionedBalances := range actual {
|
||||
s.Require().ElementsMatch(expected[address], permissionedBalances)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) TestRetrieveCollectibles() {
|
||||
m, cm, _ := s.setupManagerForTokenPermissions()
|
||||
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
package communities
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
)
|
||||
|
||||
type PermissionedBalance struct {
|
||||
Type protobuf.CommunityTokenType `json:"type"`
|
||||
Symbol string `json:"symbol"`
|
||||
Name string `json:"name"`
|
||||
Amount *bigint.BigInt `json:"amount"`
|
||||
Decimals uint64 `json:"decimals"`
|
||||
}
|
||||
|
||||
func calculatePermissionedBalancesERC20(
|
||||
accountAddresses []gethcommon.Address,
|
||||
balances BalancesByChain,
|
||||
tokenPermissions []*CommunityTokenPermission,
|
||||
) map[gethcommon.Address]map[string]*PermissionedBalance {
|
||||
res := make(map[gethcommon.Address]map[string]*PermissionedBalance)
|
||||
|
||||
// Set with composite key (chain ID + wallet address + contract address) to
|
||||
// store if we already processed the balance.
|
||||
usedBalances := make(map[string]bool)
|
||||
|
||||
for _, permission := range tokenPermissions {
|
||||
for _, criteria := range permission.TokenCriteria {
|
||||
if criteria.Type != protobuf.CommunityTokenType_ERC20 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, accountAddress := range accountAddresses {
|
||||
for chainID, hexContractAddress := range criteria.ContractAddresses {
|
||||
usedKey := strconv.FormatUint(chainID, 10) + "-" + accountAddress.Hex() + "-" + hexContractAddress
|
||||
|
||||
if _, ok := balances[chainID]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := balances[chainID][accountAddress]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
contractAddress := gethcommon.HexToAddress(hexContractAddress)
|
||||
value, ok := balances[chainID][accountAddress][contractAddress]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip the contract address if it has been used already in the sum.
|
||||
if _, ok := usedBalances[usedKey]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := res[accountAddress]; !ok {
|
||||
res[accountAddress] = make(map[string]*PermissionedBalance, 0)
|
||||
}
|
||||
if _, ok := res[accountAddress][criteria.Symbol]; !ok {
|
||||
res[accountAddress][criteria.Symbol] = &PermissionedBalance{
|
||||
Type: criteria.Type,
|
||||
Symbol: criteria.Symbol,
|
||||
Name: criteria.Name,
|
||||
Decimals: criteria.Decimals,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(0)},
|
||||
}
|
||||
}
|
||||
|
||||
res[accountAddress][criteria.Symbol].Amount.Add(
|
||||
res[accountAddress][criteria.Symbol].Amount.Int,
|
||||
value.ToInt(),
|
||||
)
|
||||
usedBalances[usedKey] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func isERC721CriteriaSatisfied(tokenBalances []thirdparty.TokenBalance, criteria *protobuf.TokenCriteria) bool {
|
||||
for _, tokenID := range criteria.TokenIds {
|
||||
tokenIDBigInt := new(big.Int).SetUint64(tokenID)
|
||||
for _, asset := range tokenBalances {
|
||||
if asset.TokenID.Cmp(tokenIDBigInt) == 0 && asset.Balance.Sign() > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func calculatePermissionedBalancesERC721(
|
||||
accountAddresses []gethcommon.Address,
|
||||
balances CollectiblesByChain,
|
||||
tokenPermissions []*CommunityTokenPermission,
|
||||
) map[gethcommon.Address]map[string]*PermissionedBalance {
|
||||
res := make(map[gethcommon.Address]map[string]*PermissionedBalance)
|
||||
|
||||
// Set with composite key (chain ID + wallet address + contract address) to
|
||||
// store if we already processed the balance.
|
||||
usedBalances := make(map[string]bool)
|
||||
|
||||
for _, permission := range tokenPermissions {
|
||||
for _, criteria := range permission.TokenCriteria {
|
||||
if criteria.Type != protobuf.CommunityTokenType_ERC721 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, accountAddress := range accountAddresses {
|
||||
for chainID, hexContractAddress := range criteria.ContractAddresses {
|
||||
usedKey := strconv.FormatUint(chainID, 10) + "-" + accountAddress.Hex() + "-" + hexContractAddress
|
||||
|
||||
if _, ok := balances[chainID]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := balances[chainID][accountAddress]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
contractAddress := gethcommon.HexToAddress(hexContractAddress)
|
||||
tokenBalances, ok := balances[chainID][accountAddress][contractAddress]
|
||||
if !ok || len(tokenBalances) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip the contract address if it has been used already in the sum.
|
||||
if _, ok := usedBalances[usedKey]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
usedBalances[usedKey] = true
|
||||
|
||||
if _, ok := res[accountAddress]; !ok {
|
||||
res[accountAddress] = make(map[string]*PermissionedBalance, 0)
|
||||
}
|
||||
if _, ok := res[accountAddress][criteria.Symbol]; !ok {
|
||||
res[accountAddress][criteria.Symbol] = &PermissionedBalance{
|
||||
Type: criteria.Type,
|
||||
Symbol: criteria.Symbol,
|
||||
Name: criteria.Name,
|
||||
Decimals: criteria.Decimals,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(0)},
|
||||
}
|
||||
}
|
||||
|
||||
if isERC721CriteriaSatisfied(tokenBalances, criteria) {
|
||||
// We don't care about summing balances, thus setting as 1 is
|
||||
// sufficient.
|
||||
res[accountAddress][criteria.Symbol].Amount = &bigint.BigInt{Int: big.NewInt(1)}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func calculatePermissionedBalances(
|
||||
chainIDs []uint64,
|
||||
accountAddresses []gethcommon.Address,
|
||||
erc20Balances BalancesByChain,
|
||||
erc721Balances CollectiblesByChain,
|
||||
tokenPermissions []*CommunityTokenPermission,
|
||||
) map[gethcommon.Address][]PermissionedBalance {
|
||||
res := make(map[gethcommon.Address][]PermissionedBalance, 0)
|
||||
|
||||
aggregatedERC721Balances := calculatePermissionedBalancesERC721(accountAddresses, erc721Balances, tokenPermissions)
|
||||
for accountAddress, tokens := range aggregatedERC721Balances {
|
||||
for _, permissionedToken := range tokens {
|
||||
if permissionedToken.Amount.Sign() > 0 {
|
||||
res[accountAddress] = append(res[accountAddress], *permissionedToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aggregatedERC20Balances := calculatePermissionedBalancesERC20(accountAddresses, erc20Balances, tokenPermissions)
|
||||
for accountAddress, tokens := range aggregatedERC20Balances {
|
||||
for _, permissionedToken := range tokens {
|
||||
if permissionedToken.Amount.Sign() > 0 {
|
||||
res[accountAddress] = append(res[accountAddress], *permissionedToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func keepRoleTokenPermissions(tokenPermissions map[string]*CommunityTokenPermission) []*CommunityTokenPermission {
|
||||
res := make([]*CommunityTokenPermission, 0)
|
||||
for _, p := range tokenPermissions {
|
||||
if p.Type == protobuf.CommunityTokenPermission_BECOME_MEMBER ||
|
||||
p.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN ||
|
||||
p.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER ||
|
||||
p.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER {
|
||||
res = append(res, p)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetPermissionedBalances returns balances indexed by account address.
|
||||
//
|
||||
// It assumes balances in different chains with the same symbol can be summed.
|
||||
// It also assumes the criteria's decimals field is the same across different
|
||||
// criteria when they refer to the same asset (by symbol).
|
||||
func (m *Manager) GetPermissionedBalances(
|
||||
ctx context.Context,
|
||||
communityID types.HexBytes,
|
||||
accountAddresses []gethcommon.Address,
|
||||
) (map[gethcommon.Address][]PermissionedBalance, error) {
|
||||
community, err := m.GetByID(communityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if community == nil {
|
||||
return nil, errors.Errorf("community does not exist ID='%s'", communityID)
|
||||
}
|
||||
|
||||
tokenPermissions := keepRoleTokenPermissions(community.TokenPermissions())
|
||||
|
||||
allChainIDs, err := m.tokenManager.GetAllChainIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountsAndChainIDs := combineAddressesAndChainIDs(accountAddresses, allChainIDs)
|
||||
|
||||
erc20TokenCriteriaByChain, erc721TokenCriteriaByChain, _ := ExtractTokenCriteria(tokenPermissions)
|
||||
|
||||
accounts := make([]gethcommon.Address, 0, len(accountsAndChainIDs))
|
||||
for _, accountAndChainIDs := range accountsAndChainIDs {
|
||||
accounts = append(accounts, accountAndChainIDs.Address)
|
||||
}
|
||||
|
||||
erc20ChainIDsSet := make(map[uint64]bool)
|
||||
erc20TokenAddresses := make([]gethcommon.Address, 0)
|
||||
for chainID, criterionByContractAddress := range erc20TokenCriteriaByChain {
|
||||
erc20ChainIDsSet[chainID] = true
|
||||
for contractAddress := range criterionByContractAddress {
|
||||
erc20TokenAddresses = append(erc20TokenAddresses, gethcommon.HexToAddress(contractAddress))
|
||||
}
|
||||
}
|
||||
|
||||
erc721ChainIDsSet := make(map[uint64]bool)
|
||||
for chainID := range erc721TokenCriteriaByChain {
|
||||
erc721ChainIDsSet[chainID] = true
|
||||
}
|
||||
|
||||
erc20ChainIDs := calculateChainIDsSet(accountsAndChainIDs, erc20ChainIDsSet)
|
||||
erc721ChainIDs := calculateChainIDsSet(accountsAndChainIDs, erc721ChainIDsSet)
|
||||
|
||||
erc20Balances, err := m.tokenManager.GetBalancesByChain(ctx, accounts, erc20TokenAddresses, erc20ChainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
erc721Balances := make(CollectiblesByChain)
|
||||
if len(erc721ChainIDs) > 0 {
|
||||
balances, err := m.GetOwnedERC721Tokens(accounts, erc721TokenCriteriaByChain, erc721ChainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
erc721Balances = balances
|
||||
}
|
||||
|
||||
return calculatePermissionedBalances(allChainIDs, accountAddresses, erc20Balances, erc721Balances, tokenPermissions), nil
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
package communities
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/protocol/requests"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
|
||||
_ "github.com/mutecomm/go-sqlcipher/v4" // require go-sqlcipher that overrides default implementation
|
||||
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
func (s *ManagerSuite) Test_calculatePermissionedBalances() {
|
||||
var mainnetID uint64 = 1
|
||||
var arbitrumID uint64 = 42161
|
||||
var gnosisID uint64 = 100
|
||||
chainIDs := []uint64{mainnetID, arbitrumID}
|
||||
|
||||
mainnetSNTContractAddress := gethcommon.HexToAddress("0xC")
|
||||
mainnetETHContractAddress := gethcommon.HexToAddress("0xA")
|
||||
arbitrumETHContractAddress := gethcommon.HexToAddress("0xB")
|
||||
mainnetTMasterAddress := gethcommon.HexToAddress("0x123")
|
||||
mainnetOwnerAddress := gethcommon.HexToAddress("0x1234")
|
||||
|
||||
account1Address := gethcommon.HexToAddress("0x1")
|
||||
account2Address := gethcommon.HexToAddress("0x2")
|
||||
account3Address := gethcommon.HexToAddress("0x3")
|
||||
accountAddresses := []gethcommon.Address{account1Address, account2Address, account3Address}
|
||||
|
||||
erc20Balances := make(BalancesByChain)
|
||||
erc721Balances := make(CollectiblesByChain)
|
||||
|
||||
erc20Balances[mainnetID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[arbitrumID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[gnosisID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
erc721Balances[mainnetID] = make(map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress)
|
||||
|
||||
// Account 1 balances
|
||||
erc20Balances[mainnetID][account1Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[mainnetID][account1Address][mainnetETHContractAddress] = intToBig(10)
|
||||
erc20Balances[arbitrumID][account1Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[arbitrumID][account1Address][arbitrumETHContractAddress] = intToBig(25)
|
||||
|
||||
// Account 2 balances
|
||||
erc20Balances[mainnetID][account2Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[mainnetID][account2Address][mainnetSNTContractAddress] = intToBig(120)
|
||||
erc721Balances[mainnetID][account2Address] = make(thirdparty.TokenBalancesPerContractAddress)
|
||||
erc721Balances[mainnetID][account2Address][mainnetTMasterAddress] = []thirdparty.TokenBalance{
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(456),
|
||||
Balance: uintToDecBig(1),
|
||||
},
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(123),
|
||||
Balance: uintToDecBig(2),
|
||||
},
|
||||
}
|
||||
erc721Balances[mainnetID][account2Address][mainnetOwnerAddress] = []thirdparty.TokenBalance{
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(100),
|
||||
Balance: uintToDecBig(6),
|
||||
},
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(101),
|
||||
Balance: uintToDecBig(1),
|
||||
},
|
||||
}
|
||||
|
||||
erc20Balances[arbitrumID][account2Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[arbitrumID][account2Address][arbitrumETHContractAddress] = intToBig(2)
|
||||
|
||||
// Account 3 balances. This account is used to assert zeroed balances are
|
||||
// removed from the final response.
|
||||
erc20Balances[mainnetID][account3Address] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[mainnetID][account3Address][mainnetETHContractAddress] = intToBig(0)
|
||||
|
||||
// A balance that should be ignored because the list of wallet addresses don't
|
||||
// contain any wallet in the Gnosis chain.
|
||||
erc20Balances[gnosisID][gethcommon.HexToAddress("0xF")] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
erc20Balances[gnosisID][gethcommon.HexToAddress("0xF")][gethcommon.HexToAddress("0x99")] = intToBig(5)
|
||||
|
||||
tokenPermissions := []*CommunityTokenPermission{
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: "1",
|
||||
TokenIds: []uint64{123, 456},
|
||||
ContractAddresses: map[uint64]string{mainnetID: mainnetTMasterAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "OWNTEST",
|
||||
Name: "Owner-Test",
|
||||
Amount: "5",
|
||||
// No account has a positive balance for these token IDs, so we
|
||||
// expect this collectible to not be present in the final result.
|
||||
TokenIds: []uint64{666},
|
||||
ContractAddresses: map[uint64]string{mainnetID: mainnetOwnerAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_ADMIN,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Amount: "20",
|
||||
Decimals: 18,
|
||||
ContractAddresses: map[uint64]string{
|
||||
arbitrumID: arbitrumETHContractAddress.Hex(),
|
||||
mainnetID: mainnetETHContractAddress.Hex(),
|
||||
},
|
||||
},
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Amount: "4",
|
||||
Decimals: 18,
|
||||
ContractAddresses: map[uint64]string{arbitrumID: arbitrumETHContractAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "SNT",
|
||||
Name: "Status",
|
||||
Amount: "1000",
|
||||
Decimals: 16,
|
||||
ContractAddresses: map[uint64]string{mainnetID: mainnetSNTContractAddress.Hex()},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&CommunityTokenPermission{
|
||||
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
||||
// Unknown permission should be ignored.
|
||||
Type: protobuf.CommunityTokenPermission_UNKNOWN_TOKEN_PERMISSION,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "DAI",
|
||||
Name: "Dai",
|
||||
Amount: "7",
|
||||
Decimals: 12,
|
||||
ContractAddresses: map[uint64]string{mainnetID: "0x1234567"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actual := calculatePermissionedBalances(
|
||||
chainIDs,
|
||||
accountAddresses,
|
||||
erc20Balances,
|
||||
erc721Balances,
|
||||
tokenPermissions,
|
||||
)
|
||||
|
||||
expected := make(map[gethcommon.Address][]PermissionedBalance)
|
||||
expected[account1Address] = []PermissionedBalance{
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Decimals: 18,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(35)},
|
||||
},
|
||||
}
|
||||
expected[account2Address] = []PermissionedBalance{
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Decimals: 18,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(2)},
|
||||
},
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "SNT",
|
||||
Name: "Status",
|
||||
Decimals: 16,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(120)},
|
||||
},
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(1)},
|
||||
},
|
||||
}
|
||||
|
||||
_, ok := actual[account1Address]
|
||||
s.Require().True(ok, "not found account1Address='%s'", account1Address)
|
||||
_, ok = actual[account1Address]
|
||||
s.Require().True(ok, "not found account2Address='%s'", account2Address)
|
||||
|
||||
for accountAddress, permissionedTokens := range actual {
|
||||
s.Require().ElementsMatch(expected[accountAddress], permissionedTokens, "accountAddress='%s'", accountAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) Test_GetPermissionedBalances() {
|
||||
m, collectiblesManager, tokenManager := s.setupManagerForTokenPermissions()
|
||||
s.Require().NotNil(m)
|
||||
s.Require().NotNil(collectiblesManager)
|
||||
|
||||
request := &requests.CreateCommunity{
|
||||
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
||||
}
|
||||
community, err := m.CreateCommunity(request, true)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(community)
|
||||
|
||||
accountAddress := gethcommon.HexToAddress("0x1")
|
||||
accountAddresses := []gethcommon.Address{accountAddress}
|
||||
|
||||
var chainID uint64 = 5
|
||||
erc20ETHAddress := gethcommon.HexToAddress("0xA")
|
||||
erc721Address := gethcommon.HexToAddress("0x123")
|
||||
|
||||
permissionRequest := &requests.CreateCommunityTokenPermission{
|
||||
CommunityID: community.ID(),
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Amount: "3",
|
||||
Decimals: 18,
|
||||
ContractAddresses: map[uint64]string{chainID: erc20ETHAddress.Hex()},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, changes, err := m.CreateCommunityTokenPermission(permissionRequest)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(changes.TokenPermissionsAdded, 1)
|
||||
|
||||
permissionRequest = &requests.CreateCommunityTokenPermission{
|
||||
CommunityID: community.ID(),
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: "1",
|
||||
TokenIds: []uint64{666},
|
||||
ContractAddresses: map[uint64]string{chainID: erc721Address.Hex()},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, changes, err = m.CreateCommunityTokenPermission(permissionRequest)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(changes.TokenPermissionsAdded, 1)
|
||||
|
||||
tokenManager.setResponse(chainID, accountAddress, erc20ETHAddress, 42)
|
||||
collectiblesManager.setResponse(chainID, accountAddress, erc721Address, []thirdparty.TokenBalance{
|
||||
thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(666),
|
||||
Balance: uintToDecBig(15),
|
||||
},
|
||||
})
|
||||
|
||||
actual, err := m.GetPermissionedBalances(context.Background(), community.ID(), accountAddresses)
|
||||
s.Require().NoError(err)
|
||||
|
||||
expected := make(map[gethcommon.Address][]PermissionedBalance)
|
||||
expected[accountAddress] = []PermissionedBalance{
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "ETH",
|
||||
Name: "Ethereum",
|
||||
Decimals: 18,
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(42)},
|
||||
},
|
||||
PermissionedBalance{
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
Symbol: "TMTEST",
|
||||
Name: "TMaster-Test",
|
||||
Amount: &bigint.BigInt{Int: big.NewInt(1)},
|
||||
},
|
||||
}
|
||||
|
||||
_, ok := actual[accountAddress]
|
||||
s.Require().True(ok, "not found accountAddress='%s'", accountAddress)
|
||||
|
||||
for address, permissionedBalances := range actual {
|
||||
s.Require().ElementsMatch(expected[address], permissionedBalances)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue