Skip to content

Commit 3d1eea1

Browse files
authored
Merge pull request #6020 from BitGo/coin-3880-map-nft-collection-id
feat(statics): map NFT collection ID to BaseCoin
2 parents ba5e3ed + 827a1ac commit 3d1eea1

File tree

6 files changed

+91
-7
lines changed

6 files changed

+91
-7
lines changed

.gitcommitscopes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
sdk-coin-rune
22
sdk-coin-sui
3+
statics

modules/bitgo/test/v2/unit/wallet.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
common,
1313
CustomSigningFunction,
1414
ECDSAUtils,
15+
EDDSAUtils,
1516
RequestTracer,
1617
TokenType,
1718
TssUtils,
@@ -34,6 +35,7 @@ import {
3435
TxRequestVersion,
3536
WalletSignMessageOptions,
3637
WalletSignTypedDataOptions,
38+
PrebuildTransactionWithIntentOptions,
3739
} from '@bitgo/sdk-core';
3840

3941
import { TestBitGo } from '@bitgo/sdk-test';
@@ -4227,6 +4229,51 @@ describe('V2 Wallet:', function () {
42274229
});
42284230
});
42294231

4232+
describe('Transfer NFTs', function () {
4233+
it('should populate intent with NFT token details', async function () {
4234+
const params: PrebuildTransactionWithIntentOptions = {
4235+
reqId,
4236+
intentType: 'payment',
4237+
recipients: [
4238+
{
4239+
address: '0x9fef749050644625012a2c866973775e7123753b3eef0a1a4037453ac26d79bf',
4240+
amount: '1',
4241+
tokenData: {
4242+
tokenType: TokenType.DIGITAL_ASSET,
4243+
tokenQuantity: '1',
4244+
tokenContractAddress: '0xbbc561fbfa5d105efd8dfb06ae3e7e5be46331165b99d518f094c701e40603b5',
4245+
tokenId: '0x675b053d72c24dcc6bc7f38cdd45b4843cfb7af69a25ad21d002c376357e9d69',
4246+
},
4247+
},
4248+
],
4249+
};
4250+
4251+
const baseCoin = bitgo.coin('tapt');
4252+
const mpcUtils = new EDDSAUtils.default(bitgo, baseCoin);
4253+
const intent = mpcUtils.populateIntent(baseCoin, params);
4254+
4255+
intent.should.have.property('intentType', 'payment');
4256+
intent.recipients!.should.deepEqual([
4257+
{
4258+
address: {
4259+
address: '0x9fef749050644625012a2c866973775e7123753b3eef0a1a4037453ac26d79bf',
4260+
},
4261+
amount: {
4262+
value: '1',
4263+
symbol: 'tapt:nftcollection1',
4264+
},
4265+
tokenData: {
4266+
tokenType: 'Digital Asset',
4267+
tokenQuantity: '1',
4268+
tokenContractAddress: '0xbbc561fbfa5d105efd8dfb06ae3e7e5be46331165b99d518f094c701e40603b5',
4269+
tokenId: '0x675b053d72c24dcc6bc7f38cdd45b4843cfb7af69a25ad21d002c376357e9d69',
4270+
tokenName: 'tapt:nftcollection1',
4271+
},
4272+
},
4273+
]);
4274+
});
4275+
});
4276+
42304277
describe('Wallet Sharing', function () {
42314278
it('should use keychain pub to share tss wallet', async function () {
42324279
const userId = '123';

modules/sdk-core/src/bitgo/utils/mpcUtils.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ import { IBaseCoin, KeychainsTriplet } from '../baseCoin';
77
import { BitGoBase } from '../bitgoBase';
88
import { AddKeychainOptions, Keychain, KeyType } from '../keychain';
99
import { encryptText, getBitgoGpgPubKey } from './opengpgUtils';
10-
import { IntentRecipient, PopulatedIntent, PrebuildTransactionWithIntentOptions } from './tss/baseTypes';
10+
import {
11+
IntentRecipient,
12+
PopulatedIntent,
13+
PrebuildTransactionWithIntentOptions,
14+
TokenTransferRecipientParams,
15+
} from './tss/baseTypes';
1116
import { envRequiresBitgoPubGpgKeyConfig, isBitgoMpcPubKey } from '../tss/bitgoPubKeys';
17+
import { coins, NetworkType } from '@bitgo/statics';
1218

1319
export interface MpcKeyShare {
1420
publicShare: string;
@@ -132,6 +138,10 @@ export abstract class MpcUtils {
132138
'token type and quantity is required to request a transaction with intent to transfer a token'
133139
);
134140
}
141+
tokenData.tokenName = this.getTokenName(baseCoin, tokenData);
142+
if (tokenData.tokenName) {
143+
formattedRecipient.amount.symbol = tokenData.tokenName;
144+
}
135145
formattedRecipient.tokenData = tokenData;
136146
}
137147
return formattedRecipient;
@@ -191,4 +201,17 @@ export abstract class MpcUtils {
191201
enableTokens: params.enableTokens,
192202
};
193203
}
204+
205+
getTokenName(baseCoin: IBaseCoin, tokenData: TokenTransferRecipientParams): string | undefined {
206+
if (tokenData.tokenName) {
207+
return tokenData.tokenName;
208+
}
209+
const networkPrefix = baseCoin.getConfig().network.type === NetworkType.TESTNET ? 't' : '';
210+
const tokenStaticsKey = `${networkPrefix}${baseCoin.getFamily()}:${tokenData.tokenContractAddress}`;
211+
if (coins.has(tokenStaticsKey)) {
212+
const tokenStatics = coins.get(tokenStaticsKey);
213+
tokenData.tokenName = tokenStatics.name;
214+
}
215+
return tokenData.tokenName;
216+
}
194217
}

modules/statics/src/account.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2646,7 +2646,7 @@ export function taptNFTCollection(
26462646
nftCollectionId: string,
26472647
asset: UnderlyingAsset,
26482648
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
2649-
prefix = '',
2649+
prefix = 't',
26502650
suffix: string = name.toUpperCase(),
26512651
network: AccountNetwork = Networks.test.apt,
26522652
primaryKeyCurve: KeyCurve = KeyCurve.Ed25519

modules/statics/src/map.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BaseCoin } from './base';
22
import { DuplicateCoinDefinitionError, CoinNotDefinedError, DuplicateCoinIdDefinitionError } from './errors';
3-
import { ContractAddressDefinedToken } from './account';
3+
import { ContractAddressDefinedToken, NFTCollectionIdDefinedToken } from './account';
44

55
export class CoinMap {
66
private readonly _map = new Map<string, Readonly<BaseCoin>>();
@@ -10,6 +10,8 @@ export class CoinMap {
1010
// map of coin by address -> the key is the family:contractAddress
1111
// the family is the where the coin is e.g l1 chains like eth, bsc etc. or l2 like arbeth, celo etc.
1212
private readonly _coinByContractAddress = new Map<string, Readonly<BaseCoin>>();
13+
// map of coin by NFT collection ID -> the key is the (t)family:nftCollectionID
14+
private readonly _coinByNftCollectionID = new Map<string, Readonly<BaseCoin>>();
1315

1416
private constructor() {
1517
// Do not instantiate
@@ -34,8 +36,12 @@ export class CoinMap {
3436
}
3537
coinMap._coinByAliases.set(alias, coin);
3638
}
37-
if (coin.isToken && coin instanceof ContractAddressDefinedToken) {
38-
coinMap._coinByContractAddress.set(`${coin.family}:${coin.contractAddress}`, coin);
39+
if (coin.isToken) {
40+
if (coin instanceof ContractAddressDefinedToken) {
41+
coinMap._coinByContractAddress.set(`${coin.family}:${coin.contractAddress}`, coin);
42+
} else if (coin instanceof NFTCollectionIdDefinedToken) {
43+
coinMap._coinByNftCollectionID.set(`${coin.prefix}${coin.family}:${coin.nftCollectionId}`, coin);
44+
}
3945
}
4046
return coinMap;
4147
}, new CoinMap());
@@ -99,7 +105,8 @@ export class CoinMap {
99105
this._map.get(key) ||
100106
this._coinByIds.get(key) ||
101107
this._coinByAliases.get(key) ||
102-
this._coinByContractAddress.get(key);
108+
this._coinByContractAddress.get(key) ||
109+
this._coinByNftCollectionID.get(key);
103110

104111
if (coin) {
105112
return coin;
@@ -113,7 +120,8 @@ export class CoinMap {
113120
this._map.has(key) ||
114121
this._coinByIds.has(key) ||
115122
this._coinByAliases.has(key) ||
116-
this._coinByContractAddress.has(key)
123+
this._coinByContractAddress.has(key) ||
124+
this._coinByNftCollectionID.has(key)
117125
);
118126
}
119127

modules/statics/test/unit/coins.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,11 @@ describe('CoinMap', function () {
637637
it('should find coin by id', () => {
638638
coins.has(btc.id).should.be.true();
639639
});
640+
641+
it('should find coin by NFT collection ID', () => {
642+
const nftCollectionStatics = coins.get('tapt:0xbbc561fbfa5d105efd8dfb06ae3e7e5be46331165b99d518f094c701e40603b5');
643+
nftCollectionStatics.name.should.eql('tapt:nftcollection1');
644+
});
640645
});
641646

642647
coins.forEach((coin, coinName) => {

0 commit comments

Comments
 (0)