Skip to content

Commit 1d6018f

Browse files
authored
Merge pull request #6102 from BitGo/COIN-4029-sdk-fetch
feat(sdk-coin-fetch): add SDK support for Fetch
2 parents a1065dd + 132abbe commit 1d6018f

File tree

13 files changed

+1068
-32
lines changed

13 files changed

+1068
-32
lines changed

modules/sdk-coin-fetch/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
"@bitgo/abstract-cosmos": "^11.7.2",
4444
"@bitgo/sdk-core": "^33.1.0",
4545
"@bitgo/statics": "^52.1.0",
46-
"@cosmjs/stargate": "^0.29.5"
46+
"@cosmjs/stargate": "^0.29.5",
47+
"@cosmjs/amino": "^0.29.5",
48+
"@cosmjs/encoding": "^0.29.5",
49+
"@cosmjs/stargate": "^0.29.5",
50+
"bignumber.js": "^9.1.1"
4751
},
4852
"devDependencies": {
4953
"@bitgo/sdk-api": "^1.62.2",

modules/sdk-coin-fetch/src/fetch.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
import { BaseCoin, BitGoBase } from '@bitgo/sdk-core';
2-
import { BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
1+
import { BaseCoin, BitGoBase, Environments } from '@bitgo/sdk-core';
2+
import { BaseUnit, BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
33
import { CosmosCoin, CosmosKeyPair, GasAmountDetails } from '@bitgo/abstract-cosmos';
4-
import { TransactionBuilderFactory } from './lib';
4+
import { KeyPair, TransactionBuilderFactory } from './lib';
5+
import { GAS_AMOUNT, GAS_LIMIT } from './lib/constants';
6+
import utils from './lib/utils';
57

8+
/**
9+
*
10+
* Full Name: Fetch
11+
* Website: https://innovationlab.fetch.ai/
12+
* Docs: https://website.prod.fetch-ai.com/docs/
13+
* GitHub : https://github.com/fetchai
14+
*/
615
export class Fetch extends CosmosCoin {
716
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
817

@@ -27,36 +36,39 @@ export class Fetch extends CosmosCoin {
2736

2837
/** @inheritDoc **/
2938
getBuilder(): TransactionBuilderFactory {
30-
throw new Error('Method not implemented.');
39+
return new TransactionBuilderFactory(coins.get(this.getChain()));
3140
}
3241

3342
/** @inheritDoc **/
3443
isValidAddress(address: string): boolean {
35-
throw new Error('Method not implemented.');
44+
return utils.isValidAddress(address) || utils.isValidValidatorAddress(address);
3645
}
3746

3847
/** @inheritDoc **/
3948
getDenomination(): string {
40-
throw new Error('Method not implemented');
49+
return BaseUnit.FETCH;
4150
}
4251

4352
/** @inheritDoc **/
4453
getGasAmountDetails(): GasAmountDetails {
45-
throw new Error('Method not implemented');
54+
return {
55+
gasAmount: GAS_AMOUNT,
56+
gasLimit: GAS_LIMIT,
57+
};
4658
}
4759

4860
/** @inheritDoc **/
4961
getKeyPair(publicKey: string): CosmosKeyPair {
50-
throw new Error('Method not implemented');
62+
return new KeyPair({ pub: publicKey });
5163
}
5264

5365
/** @inheritDoc **/
5466
protected getPublicNodeUrl(): string {
55-
throw new Error('Method not implemented');
67+
return Environments[this.bitgo.getEnv()].fetchNodeUrl;
5668
}
5769

5870
/** @inheritDoc **/
5971
getAddressFromPublicKey(pubKey: string): string {
60-
throw new Error('Method not implemented');
72+
return new KeyPair({ pub: pubKey }).getAddress();
6173
}
6274
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const validDenoms = ['fet', 'tfet', 'afet', 'atestfet'];
2+
export const accountAddressRegex = /^(fetch)1(['qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38})$/;
3+
export const validatorAddressRegex = /^(fetchvaloper)1(['qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38})$/;
4+
export const contractAddressRegex = /^(fetch)1(['qpzry9x8gf2tvdw0s3jn54khce6mua7l]+)$/;
5+
export const ADDRESS_PREFIX = 'fetch';
6+
export const GAS_AMOUNT = '100000000000000';
7+
export const GAS_LIMIT = 100000;

modules/sdk-coin-fetch/src/lib/keyPair.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { KeyPairOptions } from '@bitgo/sdk-core';
2+
import { pubkeyToAddress } from '@cosmjs/amino';
23
import { CosmosKeyPair } from '@bitgo/abstract-cosmos';
4+
import { ADDRESS_PREFIX } from './constants';
35

46
/**
57
* Fetch keys and address management.
@@ -11,6 +13,13 @@ export class KeyPair extends CosmosKeyPair {
1113

1214
/** @inheritdoc */
1315
getAddress(): string {
14-
throw new Error('Method not implemented.');
16+
const base64String = Buffer.from(this.getKeys().pub.slice(0, 66), 'hex').toString('base64');
17+
return pubkeyToAddress(
18+
{
19+
type: 'tendermint/PubKeySecp256k1',
20+
value: base64String,
21+
},
22+
ADDRESS_PREFIX
23+
);
1524
}
1625
}

modules/sdk-coin-fetch/src/lib/utils.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
import { CosmosUtils } from '@bitgo/abstract-cosmos';
2+
import { InvalidTransactionError } from '@bitgo/sdk-core';
23
import { Coin } from '@cosmjs/stargate';
4+
import BigNumber from 'bignumber.js';
5+
import * as constants from './constants';
36

47
export class Utils extends CosmosUtils {
58
/** @inheritdoc */
69
isValidAddress(address: string): boolean {
7-
throw new Error('Method not implemented.');
10+
return this.isValidCosmosLikeAddressWithMemoId(address, constants.accountAddressRegex);
811
}
912

1013
/** @inheritdoc */
1114
isValidValidatorAddress(address: string): boolean {
12-
throw new Error('Method not implemented.');
15+
return this.isValidBech32AddressMatchingRegex(address, constants.validatorAddressRegex);
1316
}
1417

1518
/** @inheritdoc */
1619
isValidContractAddress(address: string): boolean {
17-
throw new Error('Method not implemented.');
20+
return this.isValidBech32AddressMatchingRegex(address, constants.contractAddressRegex);
1821
}
1922

2023
/** @inheritdoc */
2124
validateAmount(amount: Coin): void {
22-
throw new Error('Method not implemented.');
25+
const amountBig = BigNumber(amount.amount);
26+
if (amountBig.isLessThanOrEqualTo(0)) {
27+
throw new InvalidTransactionError('transactionBuilder: validateAmount: Invalid amount: ' + amount.amount);
28+
}
29+
if (!constants.validDenoms.find((denom) => denom === amount.denom)) {
30+
throw new InvalidTransactionError('transactionBuilder: validateAmount: Invalid denom: ' + amount.denom);
31+
}
2332
}
2433
}
2534

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
export const TEST_ACCOUNT = {
2+
pubAddress: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
3+
compressedPublicKey: '02bcdbd054a73aa6097c1926c87eb7d66142d2ea710584c4b3e9844e1dab1538f0',
4+
compressedPublicKeyTwo: '02001fda4568760a99e58ee295b4a51edcc6a689297a71f7d1571cf4e1253abcde',
5+
uncompressedPublicKey:
6+
'04bcdbd054a73aa6097c1926c87eb7d66142d2ea710584c4b3e9844e1dab1538f0a088291e83cb3438431deb5e251439e338d8edc4389ea2004f442a73cc97afc8',
7+
privateKey: '3f020639e98e5c4953abb23d01a7c892e83b57593a7f46f37298b58cbf1cacd5',
8+
extendedPrv:
9+
'xprv9s21ZrQH143K3n6kDURwkfkBxB58Fxoz7cFCbpQeFws4K5iXaSUcpq18cCqJQ74MnqNrnLBHfE7YvUgKpnckmpsBLExGSRK55Ud5uuxGrxL',
10+
extendedPub:
11+
'xpub661MyMwAqRbcGGBDKVxx7ogvWCucfRXqUqAoQCpFpHQ3Bt3g7ynsNdKcTWvGGYrpq6VYPPgxjMfKaszXMhKmmCZEnhg9RpqcGeP9uCnZsD9',
12+
};
13+
14+
export const TEST_SEND_TX = {
15+
hash: 'C23206B4F9A58AE97709913C292CA3C8414705B1752A85420ECCD48477C994A0',
16+
signature: 'yu/xu+ZErYiD8kz0yLSxN8vcb0c2czjiQZ5izzs+L1Ij0qpZ5c6+WBbA0v46pPe18Uy2A7bnsvG+F94PjDcmPA==',
17+
pubKey: 'AhbGVZ/tNWMeQdYLoTILXP6yrP/JuV9O1MahYUTV/fbX',
18+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
19+
signedTxBase64:
20+
'CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLGZldGNoMXY2MHBobHV4eHY4NXdycnEya2puaHczMzZqNjhjdGR4ZzU5dmttEixmZXRjaDE3NTNmNzhmMmNwNjdzM2RkODkzZDBnbjBmYTNyeTBybmM2eTJ6dRoRCghhdGVzdGZldBIFMTAwMDASdQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAhbGVZ/tNWMeQdYLoTILXP6yrP/JuV9O1MahYUTV/fbXEgQKAggBGAgSIQobCghhdGVzdGZldBIPMTAwMDAwMDAwMDAwMDAwEKCNBhpAyu/xu+ZErYiD8kz0yLSxN8vcb0c2czjiQZ5izzs+L1Ij0qpZ5c6+WBbA0v46pPe18Uy2A7bnsvG+F94PjDcmPA==',
21+
sender: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
22+
recipient: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
23+
chainId: 'dorado-1',
24+
accountNumber: 8,
25+
sequence: 8,
26+
sendAmount: '10000',
27+
feeAmount: '100000000000000',
28+
sendMessage: {
29+
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
30+
value: {
31+
amount: [
32+
{
33+
denom: 'atestfet',
34+
amount: '10000',
35+
},
36+
],
37+
toAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
38+
fromAddress: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
39+
},
40+
},
41+
gasBudget: {
42+
amount: [{ denom: 'atestfet', amount: '100000000000000' }],
43+
gasLimit: 100000,
44+
},
45+
};
46+
47+
export const TEST_SEND_TX2 = {
48+
hash: '58136389A3FCA2F7600ADDA5A168408F2E684E141FCF9E841910B2A210EA174B',
49+
signature: 'UX+f9159YGdnpVghlSHRVQvKcGT+qxoGcUkQs6lHtV9eHXA6HLpGGrMwL/dN6XJBrthglPrCXSahNcRNZy3hgw==',
50+
pubKey: 'AhbGVZ/tNWMeQdYLoTILXP6yrP/JuV9O1MahYUTV/fbX',
51+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
52+
signedTxBase64:
53+
'CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLGZldGNoMXY2MHBobHV4eHY4NXdycnEya2puaHczMzZqNjhjdGR4ZzU5dmttEixmZXRjaDE3NTNmNzhmMmNwNjdzM2RkODkzZDBnbjBmYTNyeTBybmM2eTJ6dRoRCghhdGVzdGZldBIFMTAwMDASdQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAhbGVZ/tNWMeQdYLoTILXP6yrP/JuV9O1MahYUTV/fbXEgQKAggBGBESIQobCghhdGVzdGZldBIPMTAwMDAwMDAwMDAwMDAwEKCNBhpAUX+f9159YGdnpVghlSHRVQvKcGT+qxoGcUkQs6lHtV9eHXA6HLpGGrMwL/dN6XJBrthglPrCXSahNcRNZy3hgw==',
54+
sender: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
55+
recipient: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
56+
chainId: 'dorado-1',
57+
accountNumber: 17,
58+
sequence: 17,
59+
sendAmount: '10000',
60+
feeAmount: '100000000000000',
61+
sendMessage: {
62+
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
63+
value: {
64+
amount: [
65+
{
66+
denom: 'atestfet',
67+
amount: '10000',
68+
},
69+
],
70+
toAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
71+
fromAddress: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
72+
},
73+
},
74+
gasBudget: {
75+
amount: [{ denom: 'atestfet', amount: '100000000000000' }],
76+
gasLimit: 100000,
77+
},
78+
};
79+
80+
export const TEST_SEND_MANY_TX = {
81+
hash: 'E3EE15DF1FB4C4DCCE4F72FA524C33DE8FAB937A4CC43D5F7AF8747398A5B766',
82+
signature: 'Ay9E6/64xRYmP4aPDgquNm16ZRIsFaYSTWRQCS1YWWgqnuOGwCWKYUeFLSZROBjUhx26MZX7tzLXSmDQbpnQug==',
83+
pubKey: 'AhbGVZ/tNWMeQdYLoTILXP6yrP/JuV9O1MahYUTV/fbX',
84+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
85+
signedTxBase64:
86+
'CqQCCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLGZldGNoMXY2MHBobHV4eHY4NXdycnEya2puaHczMzZqNjhjdGR4ZzU5dmttEixmZXRjaDE3NTNmNzhmMmNwNjdzM2RkODkzZDBnbjBmYTNyeTBybmM2eTJ6dRoRCghhdGVzdGZldBIFMTAwMDAKjwEKHC9jb3Ntb3MuYmFuay52MWJldGExLk1zZ1NlbmQSbwosZmV0Y2gxdjYwcGhsdXh4djg1d3JycTJram5odzMzNmo2OGN0ZHhnNTl2a20SLGZldGNoMTAzNTI5ZGF4NXM2anUwNHhnZmtxdTBhZWhhYWd5bjQ1bWRjd3JlGhEKCGF0ZXN0ZmV0EgUxMDAwMBJ1ClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECFsZVn+01Yx5B1guhMgtc/rKs/8m5X07UxqFhRNX99tcSBAoCCAEYEBIhChsKCGF0ZXN0ZmV0Eg8xMDAwMDAwMDAwMDAwMDAQoI0GGkADL0Tr/rjFFiY/ho8OCq42bXplEiwVphJNZFAJLVhZaCqe44bAJYphR4UtJlE4GNSHHboxlfu3MtdKYNBumdC6',
87+
sender: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
88+
chainId: 'dorado-1',
89+
accountNumber: 16,
90+
sequence: 16,
91+
memo: '',
92+
sendMessages: [
93+
{
94+
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
95+
value: {
96+
amount: [
97+
{
98+
denom: 'atestfet',
99+
amount: '10000',
100+
},
101+
],
102+
toAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
103+
fromAddress: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
104+
},
105+
},
106+
{
107+
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
108+
value: {
109+
amount: [
110+
{
111+
denom: 'atestfet',
112+
amount: '10000',
113+
},
114+
],
115+
toAddress: 'fetch103529dax5s6ju04xgfkqu0aehaagyn45mdcwre',
116+
fromAddress: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
117+
},
118+
},
119+
],
120+
gasBudget: {
121+
amount: [{ denom: 'atestfet', amount: '100000000000000' }],
122+
gasLimit: 100000,
123+
},
124+
};
125+
126+
export const TEST_TX_WITH_MEMO = {
127+
hash: 'E439A7F40701015BE34E2F1EC60065CDF001E86FD49806C53F0844C992965442',
128+
signature: '5LP2fhRkAb+G/HNgQjyLkU+aIrVuk6yYkq6IU/JI9SBhtju441MOjS9XQ5ZZ6WT9t+muP6a8H0nQ1ZKtvQNZkg==',
129+
pubKey: 'AhbGVZ/tNWMeQdYLoTILXP6yrP/JuV9O1MahYUTV/fbX',
130+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
131+
signedTxBase64:
132+
'CpMBCosBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmsKLGZldGNoMXY2MHBobHV4eHY4NXdycnEya2puaHczMzZqNjhjdGR4ZzU5dmttEixmZXRjaDE3NTNmNzhmMmNwNjdzM2RkODkzZDBnbjBmYTNyeTBybmM2eTJ6dRoNCghhdGVzdGZldBIBMRIDMjQxEnUKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQIWxlWf7TVjHkHWC6EyC1z+sqz/yblfTtTGoWFE1f321xIECgIIARgSEiEKGwoIYXRlc3RmZXQSDzEwMDAwMDAwMDAwMDAwMBCgjQYaQOSz9n4UZAG/hvxzYEI8i5FPmiK1bpOsmJKuiFPySPUgYbY7uONTDo0vV0OWWelk/bfprj+mvB9J0NWSrb0DWZI=',
133+
from: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
134+
to: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
135+
chainId: 'dorado-1',
136+
accountNumber: 18,
137+
sequence: 18,
138+
feeAmount: '100000000000000',
139+
sendAmount: '1',
140+
sendMessage: {
141+
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
142+
value: {
143+
amount: [
144+
{
145+
denom: 'atestfet',
146+
amount: '1',
147+
},
148+
],
149+
fromAddress: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
150+
toAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
151+
},
152+
},
153+
memo: '241',
154+
gasBudget: {
155+
amount: [{ denom: 'atestfet', amount: '100000000000000' }],
156+
gasLimit: 100000,
157+
},
158+
};
159+
160+
export const address = {
161+
address1: 'fetch1v60phluxxv85wrrq2kjnhw336j68ctdxg59vkm',
162+
address2: 'fetch17xpfvakm2amg962yls6f84z3kell8c5lry2yf2',
163+
address3: 'fetch17xpfvakm2amg962yls6f84z3kell8c5lry2-yf2',
164+
address4: 'fetch103529dax5s6ju04xgfkqu0332agyn45mdcwre',
165+
address5: 'fetch103529daxcd5s6ju04xgfkqu0aehaagyn45mdcwre',
166+
address6: 'fetch103529dax5s6ju04xgfkqu0aehaagyn45mdcwre.9pvdadzax7durfm7hlfn8',
167+
validatorAddress1: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
168+
validatorAddress2: 'fetchvaloper1je7r8yuqgaf5f2tx4z2f9008wp4jx0ct6msnzg',
169+
validatorAddress3: 'fetchvaloper1edqmkwy4rh87020rvf9xn7kktyu7x894l#d46w',
170+
validatorAddress4: 'fetchvaloper1edqmkw2y4rh87020rvf9xn7kktyu7x894led46w',
171+
noMemoIdAddress: 'fetch17xpfvakm2amg962yls6f84z3kell8c5lry2yf2',
172+
validMemoIdAddress: 'fetch17xpfvakm2amg962yls6f84z3kell8c5lry2yf2?memoId=2',
173+
invalidMemoIdAddress: 'fetch17xpfvakm2amg962yls6f84z3kell8c5lry2yf2?memoId=1.23',
174+
multipleMemoIdAddress: 'fetch17xpfvakm2amg962yls6f84z3kell8c5lry2yf2?memoId=3&memoId=12',
175+
};
176+
177+
export const blockHash = {
178+
hash1: 'E9FB894F357A88F33C3A3097A1354CFE0BFB4649AF07C032A590E549A564CED7',
179+
hash2: 'BD5B88D9DFE34C888CAA12C35A837A96CB420B422F21FD2C451BC3F6B9A446B1',
180+
};
181+
182+
export const txIds = {
183+
hash1: '58136389A3FCA2F7600ADDA5A168408F2E684E141FCF9E841910B2A210EA174B',
184+
hash2: '7113A3B0757EDB21F7503F777B0520A25FA23D39CB4C7FB1265F69FBA32E601C',
185+
hash3: 'C23206B4F9A58AE97709913C292CA3C8414705B1752A85420ECCD48477C994A0',
186+
};
187+
188+
export const coinAmounts = {
189+
amount1: { amount: '100000', denom: 'atestfet' },
190+
amount2: { amount: '1000000', denom: 'atestfet' },
191+
amount3: { amount: '10000000', denom: 'atestfet' },
192+
amount4: { amount: '-1', denom: 'atestfet' },
193+
amount5: { amount: '1000000000', denom: 'matestfet' },
194+
};

0 commit comments

Comments
 (0)