@sentio/cli 2.4.0 → 2.5.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/abi.ts ADDED
@@ -0,0 +1,150 @@
1
+ import { AptosClient } from 'aptos-sdk'
2
+ import { JsonRpcProvider } from '@mysten/sui.js'
3
+ import { CHAIN_IDS } from './chain.js'
4
+ import chalk from 'chalk'
5
+ import process from 'process'
6
+ import path from 'path'
7
+ import fs from 'fs-extra'
8
+ // @ts-ignore no types
9
+ import { init } from 'etherscan-api'
10
+
11
+ export async function getABI(
12
+ chain: string,
13
+ address: string,
14
+ name: string | undefined
15
+ ): Promise<{ name?: string; abi: object | string }> {
16
+ let ethApi
17
+ let aptosClient: AptosClient | undefined
18
+ let suiClient: JsonRpcProvider | undefined
19
+
20
+ switch (chain) {
21
+ case CHAIN_IDS.APTOS_MAINNET:
22
+ aptosClient = new AptosClient('https://mainnet.aptoslabs.com/')
23
+ break
24
+ case CHAIN_IDS.APTOS_TESTNET:
25
+ aptosClient = new AptosClient('https://testnet.aptoslabs.com/')
26
+ break
27
+ case CHAIN_IDS.SUI_MAINNET:
28
+ throw Error('SUI mainnet is not support yet, try sui/testnet')
29
+ // suiClient = new JsonRpcProvider('https://fullnode.mainnet.sui.io/')
30
+ // break
31
+ case CHAIN_IDS.SUI_TESTNET:
32
+ suiClient = new JsonRpcProvider('https://fullnode.testnet.sui.io/')
33
+ break
34
+ case CHAIN_IDS.ETHEREUM:
35
+ ethApi = init()
36
+ break
37
+ // case CHAIN_IDS.ROPSTEN:
38
+ // ethApi = init(undefined, "ropsten")
39
+ // break
40
+ // case CHAIN_IDS.RINKEBY:
41
+ // ethApi = init(undefined, "rinkeby")
42
+ // break
43
+ case CHAIN_IDS.GOERLI:
44
+ ethApi = init(undefined, 'goerli')
45
+ break
46
+ // case CHAIN_IDS.SEPOLIA:
47
+ // ethApi = init(undefined, "sepolia")
48
+ // break
49
+
50
+ case CHAIN_IDS.ARBITRUM:
51
+ ethApi = init(undefined, 'arbitrum')
52
+ break
53
+ case CHAIN_IDS.ARBITRUM_GOERLI:
54
+ ethApi = init(undefined, 'arbitrum_rinkeby')
55
+ break
56
+ case CHAIN_IDS.AVALANCHE:
57
+ ethApi = init(undefined, 'avalanche')
58
+ break
59
+ // case CHAIN_IDS.KOVAN:
60
+ // ethApi = init(undefined, "kovan")
61
+ // break
62
+ default:
63
+ console.error(chalk.red(`chain ${chain} not supported for direct add, please download the ABI manually`))
64
+ process.exit(1)
65
+ }
66
+
67
+ const baseErrMsg = chalk.red(
68
+ `Failed to automatic download contract ${address} from ${chain}, please manually download abi and put it into abis/eth directory`
69
+ )
70
+
71
+ if (aptosClient) {
72
+ try {
73
+ return {
74
+ abi: await aptosClient.getAccountModules(address),
75
+ }
76
+ } catch (e) {
77
+ console.error(baseErrMsg, e)
78
+ process.exit(1)
79
+ }
80
+ }
81
+ if (suiClient) {
82
+ try {
83
+ return {
84
+ abi: await suiClient.getNormalizedMoveModulesByPackage(address),
85
+ }
86
+ } catch (e) {
87
+ console.error(baseErrMsg, e)
88
+ process.exit(1)
89
+ }
90
+ }
91
+
92
+ try {
93
+ let resp = await ethApi.contract.getabi(address)
94
+ if (resp.status !== '1') {
95
+ throw Error(resp.message)
96
+ }
97
+ const abi = resp.result
98
+
99
+ if (!name) {
100
+ await new Promise((resolve) => setTimeout(resolve, 1000))
101
+ resp = await ethApi.contract.getsourcecode(address)
102
+ if (resp.status !== '1') {
103
+ throw Error(resp.message)
104
+ }
105
+ const contractName = resp.result[0].ContractName
106
+ if (contractName) {
107
+ name = contractName
108
+ }
109
+ }
110
+ return {
111
+ name,
112
+ abi,
113
+ }
114
+ } catch (e) {
115
+ console.error(baseErrMsg, e)
116
+ process.exit(1)
117
+ }
118
+ }
119
+
120
+ export function getABIFilePath(chain: string, name: string): string {
121
+ let subpath
122
+ switch (chain) {
123
+ case CHAIN_IDS.APTOS_MAINNET:
124
+ subpath = 'aptos'
125
+ break
126
+ case CHAIN_IDS.APTOS_TESTNET:
127
+ subpath = 'aptos/test'
128
+ break
129
+ case CHAIN_IDS.SUI_MAINNET:
130
+ subpath = 'sui'
131
+ break
132
+ case CHAIN_IDS.SUI_TESTNET:
133
+ subpath = 'sui/test'
134
+ break
135
+ default:
136
+ subpath = 'eth'
137
+ }
138
+
139
+ return path.join('abis', subpath, name + '.json')
140
+ }
141
+
142
+ export function writeABIFile(obj: string | object, output: string) {
143
+ if (typeof obj === 'string') {
144
+ obj = JSON.parse(obj)
145
+ }
146
+ const data = JSON.stringify(obj, null, 2)
147
+ fs.mkdirSync(path.dirname(output), { recursive: true })
148
+ fs.writeFileSync(output, data)
149
+ console.log(chalk.green('ABI has been downloaded to', output))
150
+ }
package/src/chain.ts ADDED
@@ -0,0 +1,195 @@
1
+ // copy from https://github.com/DefiLlama/chainlist/blob/main/constants/chainIds.js
2
+ // and https://besu.hyperledger.org/en/stable/Concepts/NetworkID-And-ChainID/
3
+
4
+ export const CHAIN_IDS = {
5
+ KARDIA: '0',
6
+ ETHEREUM: '1',
7
+ EXPANSE: '2',
8
+ ROPSTEN: '3',
9
+ RINKEBY: '4',
10
+ GOERLI: '5',
11
+ KOTTI: '6',
12
+ UBIQ: '8',
13
+ OPTIMISM: '10',
14
+ SONGBIRD: '19',
15
+ ELASTOS: '20',
16
+ CRONOS: '25',
17
+ RSK: '30',
18
+ TELOS: '40',
19
+ XDC: '50',
20
+ CSC: '52',
21
+ ZYX: '55',
22
+ BINANCE: '56',
23
+ SYSCOIN: '57',
24
+ GOCHAIN: '60',
25
+ ETHCLASSIC: '61',
26
+ MORDOR: '63',
27
+ OKEXCHAIN: '66',
28
+ HOO: '70',
29
+ METER: '82',
30
+ TOMOCHAIN: '88',
31
+ BINANCE_TEST: '97',
32
+ XDAI: '100',
33
+ VELAS: '106',
34
+ THUNDERCORE: '108',
35
+ FUSE: '122',
36
+ HECO: '128',
37
+ POLYGON: '137',
38
+ XDAIARB: '200',
39
+ ASTOR: '212',
40
+ ENERGYWEB: '246',
41
+ FANTOM: '250',
42
+ HPB: '269',
43
+ BOBA: '288',
44
+ KUCOIN: '321',
45
+ ZKSYNC_ERA: '324',
46
+ SHIDEN: '336',
47
+ THETA: '361',
48
+ SX: '416',
49
+ CANDLE: '534',
50
+ ASTAR: '592',
51
+ CALLISTO: '820',
52
+ WANCHAIN: '888',
53
+ POLYGON_ZKEVM: '1101',
54
+ METIS: '1088',
55
+ OMCHAIN: '1246',
56
+ MOONBEAM: '1284',
57
+ MOONRIVER: '1285',
58
+ MOONBASE: '1287',
59
+ DEV: '2018',
60
+ RONIN: '2020',
61
+ KAVA: '2222',
62
+ EZCHAIN: '2612',
63
+ PHI: '4181',
64
+ IOTEX: '4689',
65
+ XLC: '5050',
66
+ NAHMII: '5551',
67
+ NMACTEST: '7777',
68
+ KLAYTN: '8217',
69
+ EVMOS: '9001',
70
+ BASE_GOERLI: '84531',
71
+ SMARTBCH: '10000',
72
+ CRYSTALEUM: '103090',
73
+ FUSION: '32659',
74
+ ARBITRUM: '42161',
75
+ CELO: '42220',
76
+ OASIS: '42262',
77
+ AVALANCHE: '43114',
78
+ GODWOKEN: '71402',
79
+ AKROMA: '200625',
80
+ POLIS: '333999',
81
+ ARBITRUM_GOERLI: '421613',
82
+ SEPOLIA: '11155111',
83
+ AURORA: '1313161554',
84
+ HARMONY: '1666600000',
85
+ PALM: '11297108109',
86
+ CURIO: '836542336838601',
87
+ SOLANA_MAINNET: 'sol_mainnet',
88
+ SOLANA_DEVNET: 'sol_devnet',
89
+ SOLANA_TESTNET: 'sol_testnet',
90
+ SOLANA_PYTH: 'sol_pyth',
91
+ SUI_MAINNET: 'sui_mainnet',
92
+ SUI_TESTNET: 'sui_testnet',
93
+ SUI_DEVNET: 'sui_devnet',
94
+ APTOS_MAINNET: 'aptos_mainnet',
95
+ APTOS_TESTNET: 'aptos_testnet',
96
+ APTOS_DEVNET: 'aptos_devnet',
97
+ }
98
+
99
+ export const CHAIN_MAP: Record<string, string> = {}
100
+
101
+ for (const [key, value] of Object.entries(CHAIN_IDS)) {
102
+ if (value === CHAIN_IDS.POLYGON_ZKEVM) {
103
+ CHAIN_MAP[value] = 'Polygon zkEVM'
104
+ } else if (value === CHAIN_IDS.ZKSYNC_ERA) {
105
+ CHAIN_MAP[value] = 'zkSync Era'
106
+ } else {
107
+ const parts = key.split('_')
108
+ CHAIN_MAP[value] = parts
109
+ .map((part, index) => {
110
+ return part[0] + part.slice(1).toLowerCase()
111
+ })
112
+ .join(' ')
113
+ }
114
+ }
115
+
116
+ export function getChainName(chainId: string | number | null | undefined): string {
117
+ if (typeof chainId === 'number') {
118
+ chainId = chainId.toString()
119
+ }
120
+ if (chainId) {
121
+ const name = CHAIN_MAP[chainId]
122
+ if (name) {
123
+ return name
124
+ }
125
+ }
126
+ return chainId || ''
127
+ }
128
+
129
+ export function getChainType(chainId?: string | number): string {
130
+ const id = String(chainId).toLowerCase()
131
+ if (id.startsWith('sol')) {
132
+ return 'solana'
133
+ }
134
+ if (id.startsWith('sui')) {
135
+ return 'sui'
136
+ }
137
+ if (id.startsWith('apt')) {
138
+ return 'aptos'
139
+ }
140
+ return 'EVM'
141
+ }
142
+
143
+ export function getChainScanUrl(
144
+ chainId?: string | number,
145
+ hash?: string,
146
+ subtype?: 'block' | 'address' | 'transaction'
147
+ ): string | undefined {
148
+ const chainName = getChainName(chainId)
149
+ if (chainName === 'Ethereum') {
150
+ if (subtype === 'block') {
151
+ return `https://etherscan.io/block/${hash}`
152
+ } else if (subtype === 'address') {
153
+ return `https://etherscan.io/address/${hash}`
154
+ } else {
155
+ return `https://etherscan.io/tx/${hash}`
156
+ }
157
+ }
158
+ if (chainName === 'Polygon') {
159
+ if (subtype === 'block') {
160
+ return `https://polygonscan.com/block/${hash}`
161
+ } else if (subtype === 'address') {
162
+ return `https://polygonscan.com/address/${hash}`
163
+ } else {
164
+ return `https://polygonscan.com/tx/${hash}`
165
+ }
166
+ }
167
+ if (chainName.startsWith('Aptos ')) {
168
+ if (subtype === 'block') {
169
+ return `https://explorer.aptos.io/block/${hash}`
170
+ } else if (subtype === 'address') {
171
+ return `https://explorer.aptos.io/account/${hash}`
172
+ } else {
173
+ return `https://explorer.aptos.io/txn/${hash}`
174
+ }
175
+ }
176
+ if (chainName.startsWith('Solana ')) {
177
+ if (subtype === 'block') {
178
+ return `https://explorer.solana.com/block/${hash}`
179
+ } else if (subtype === 'address') {
180
+ return `https://explorer.solana.com/address/${hash}`
181
+ } else {
182
+ return `https://explorer.solana.io/tx/${hash}`
183
+ }
184
+ }
185
+ if (chainName.startsWith('Sui ')) {
186
+ if (subtype === 'block') {
187
+ return `https://explorer.sui.io/block/${hash}`
188
+ } else if (subtype === 'address') {
189
+ return `https://explorer.sui.io/address/${hash}`
190
+ } else {
191
+ return `https://explorer.sui.io/transaction/${hash}`
192
+ }
193
+ }
194
+ return undefined
195
+ }
package/src/cli.ts CHANGED
@@ -5,10 +5,10 @@ import commandLineUsage from 'command-line-usage'
5
5
  import fs from 'fs'
6
6
  import path from 'path'
7
7
 
8
- import yaml from 'js-yaml'
9
- import { SentioProjectConfig } from './config.js'
8
+ import yaml from 'yaml'
9
+ import { YamlProjectConfig } from './config.js'
10
10
  import chalk from 'chalk'
11
- import { buildProcessor, buildProcessorWithArgs } from './commands/build.js'
11
+ import { buildProcessorWithArgs, generate } from './commands/build.js'
12
12
  import { runCreate } from './commands/run-create.js'
13
13
  import { runVersion } from './commands/run-version.js'
14
14
  import { runLogin } from './commands/run-login.js'
@@ -41,7 +41,7 @@ if (mainOptions.command === 'login') {
41
41
  // TODO move them to their own modules
42
42
 
43
43
  // Process configs
44
- let processorConfig: SentioProjectConfig = { host: '', project: '', build: true, debug: false }
44
+ let processorConfig: YamlProjectConfig = { host: '', project: '', build: true, debug: false, contracts: [] }
45
45
  // Fist step, read from project yaml file
46
46
  try {
47
47
  console.log(chalk.blue('Loading Process config'))
@@ -59,7 +59,7 @@ if (mainOptions.command === 'login') {
59
59
  process.exit(1)
60
60
  }
61
61
 
62
- processorConfig = yaml.load(fs.readFileSync('sentio.yaml', 'utf8')) as SentioProjectConfig
62
+ processorConfig = yaml.parse(fs.readFileSync('sentio.yaml', 'utf8')) as YamlProjectConfig
63
63
  if (!processorConfig.project === undefined) {
64
64
  console.error('Config yaml must have contain a valid project identifier')
65
65
  process.exit(1)
@@ -95,7 +95,7 @@ if (mainOptions.command === 'login') {
95
95
  } else if (mainOptions.command === 'build') {
96
96
  await buildProcessorWithArgs(argv)
97
97
  } else if (mainOptions.command === 'gen') {
98
- await buildProcessor(true, [])
98
+ await generate(argv)
99
99
  } else {
100
100
  usage()
101
101
  }
@@ -6,6 +6,9 @@ import * as process from 'process'
6
6
  import { getPackageRoot } from '../utils.js'
7
7
  import commandLineArgs from 'command-line-args'
8
8
  import commandLineUsage from 'command-line-usage'
9
+ import yaml from 'yaml'
10
+ import { YamlProjectConfig } from '../config.js'
11
+ import { getABIFilePath, getABI, writeABIFile } from '../abi.js'
9
12
 
10
13
  export const buildOptionDefinitions = [
11
14
  {
@@ -24,14 +27,27 @@ export const buildOptionDefinitions = [
24
27
  type: Boolean,
25
28
  description: 'Skip dependency enforce.',
26
29
  },
30
+ {
31
+ name: 'example',
32
+ type: Boolean,
33
+ description: 'Generate example usage of the processor.',
34
+ },
35
+ ]
36
+
37
+ export const GenOptionDefinitions = [
38
+ {
39
+ name: 'example',
40
+ type: Boolean,
41
+ description: 'Generate example usage of the processor.',
42
+ },
27
43
  ]
28
44
 
29
45
  export async function buildProcessorWithArgs(argv: string[]) {
30
46
  const options = commandLineArgs(buildOptionDefinitions, { argv })
31
47
  const usage = commandLineUsage([
32
48
  {
33
- header: 'Create a template project',
34
- content: 'sentio create <name>',
49
+ header: 'Build project',
50
+ content: 'sentio build',
35
51
  },
36
52
  {
37
53
  header: 'Options',
@@ -46,13 +62,33 @@ export async function buildProcessorWithArgs(argv: string[]) {
46
62
  await buildProcessor(false, options)
47
63
  }
48
64
 
65
+ export async function generate(argv: string[]) {
66
+ const options = commandLineArgs(GenOptionDefinitions, { argv })
67
+ const usage = commandLineUsage([
68
+ {
69
+ header: 'Generate type binding',
70
+ content: 'sentio gen [--example]',
71
+ },
72
+ {
73
+ header: 'Options',
74
+ optionList: GenOptionDefinitions,
75
+ },
76
+ ])
77
+
78
+ if (options.help) {
79
+ console.log(usage)
80
+ process.exit(0)
81
+ }
82
+ await buildProcessor(true, options)
83
+ }
84
+
49
85
  export async function buildProcessor(onlyGen: boolean, options: commandLineArgs.CommandLineOptions) {
50
86
  if (!options['skip-deps'] && !onlyGen) {
51
87
  await installDeps()
52
88
  }
53
89
 
54
90
  if (!options['skip-gen']) {
55
- await codegen()
91
+ await codegen(options.example || false)
56
92
  }
57
93
 
58
94
  if (!onlyGen) {
@@ -87,8 +123,21 @@ import 'mine.js'
87
123
  }
88
124
  }
89
125
 
90
- export async function codegen() {
126
+ export async function codegen(genExample: boolean) {
127
+ const processorConfig = yaml.parse(fs.readFileSync('sentio.yaml', 'utf8')) as YamlProjectConfig
128
+
129
+ for (const contract of processorConfig.contracts || []) {
130
+ const outputPath = getABIFilePath(contract.chain, contract.name || contract.address)
131
+ if (fs.existsSync(outputPath)) {
132
+ continue
133
+ }
134
+ console.log('Download Missing ABI specified in sentio.yaml')
135
+ const res = await getABI(contract.chain, contract.address, contract.name)
136
+ writeABIFile(res.abi, outputPath)
137
+ }
138
+
91
139
  const outputBase = path.resolve('src', 'types')
140
+
92
141
  try {
93
142
  // @ts-ignore dynamic import
94
143
  const codegen = await import('@sentio/sdk/eth/codegen')
@@ -100,7 +149,7 @@ export async function codegen() {
100
149
  }
101
150
  fs.emptyDirSync(output)
102
151
  // @ts-ignore dynamic import
103
- await codegen.codegen(input, output)
152
+ await codegen.codegen(input, output, processorConfig.contracts)
104
153
  } catch (e) {
105
154
  console.error('code gen error', e)
106
155
  }
@@ -113,7 +162,7 @@ export async function codegen() {
113
162
  fs.emptyDirSync(output)
114
163
 
115
164
  // @ts-ignore dynamic import
116
- await codegen.codegen(path.resolve('abis', 'solana'), output)
165
+ await codegen.codegen(path.resolve('abis', 'solana'), output, genExample)
117
166
  } catch (e) {
118
167
  console.error('code gen error', e)
119
168
  }
@@ -126,7 +175,7 @@ export async function codegen() {
126
175
  fs.emptyDirSync(output)
127
176
 
128
177
  // @ts-ignore dynamic import
129
- await codegen.codegen(path.resolve('abis', 'aptos'), output)
178
+ await codegen.codegen(path.resolve('abis', 'aptos'), output, genExample)
130
179
  } catch (e) {
131
180
  console.error('code gen error', e)
132
181
  }
@@ -139,7 +188,7 @@ export async function codegen() {
139
188
  fs.emptyDirSync(output)
140
189
 
141
190
  // @ts-ignore dynamic import
142
- await codegen.codegen(path.resolve('abis', 'sui'), output)
191
+ await codegen.codegen(path.resolve('abis', 'sui'), output, genExample)
143
192
  } catch (e) {
144
193
  console.error('code gen error', e)
145
194
  }
@@ -1,15 +1,13 @@
1
1
  import commandLineArgs from 'command-line-args'
2
2
  import commandLineUsage from 'command-line-usage'
3
- import path from 'path'
4
3
  import fs from 'fs-extra'
5
4
  import chalk from 'chalk'
6
5
 
7
6
  import { codegen } from './build.js'
8
7
 
9
- // @ts-ignore no types
10
- import { init } from 'etherscan-api'
11
- import { AptosClient } from 'aptos-sdk'
12
- import { JsonRpcProvider } from '@mysten/sui.js'
8
+ import * as process from 'process'
9
+ import yaml from 'yaml'
10
+ import { getABIFilePath, getABI, writeABIFile } from '../abi.js'
13
11
 
14
12
  export async function runAdd(argv: string[]) {
15
13
  const optionDefinitions = [
@@ -23,9 +21,9 @@ export async function runAdd(argv: string[]) {
23
21
  name: 'chain',
24
22
  alias: 'c',
25
23
  type: String,
26
- defaultValue: 'homestead',
24
+ defaultValue: '1',
27
25
  description:
28
- 'Chain identifier, can be: homestead/mainnet,goerli,arbitrum,avalanche, apots, aptos/testnet and sui, sui/testnet',
26
+ 'Chain id define from here: https://raw.githubusercontent.com/sentioxyz/sentio-sdk/main/packages/sdk/src/core/chain.ts notice some chain may not support this command',
29
27
  },
30
28
  {
31
29
  name: 'address',
@@ -64,91 +62,38 @@ export async function runAdd(argv: string[]) {
64
62
  process.exit(1)
65
63
  }
66
64
 
67
- let ethApi
68
- let aptosClient: AptosClient | undefined
69
- let suiClient: JsonRpcProvider | undefined
70
- switch (chain) {
71
- case 'aptos':
72
- aptosClient = new AptosClient('https://mainnet.aptoslabs.com/')
73
- break
74
- case 'aptos/testnet':
75
- aptosClient = new AptosClient('https://testnet.aptoslabs.com/')
76
- break
77
- case 'sui':
78
- throw Error('SUI mainnet is not support yet, try sui/testnet')
79
- // suiClient = new JsonRpcProvider('https://fullnode.mainnet.sui.io/')
80
- // break
81
- case 'sui/testnet':
82
- suiClient = new JsonRpcProvider('https://fullnode.testnet.sui.io/')
83
- break
84
- case 'mainnet':
85
- case 'homestead':
86
- ethApi = init()
87
- break
88
- default:
89
- ethApi = init(undefined, chain)
90
- }
91
-
92
- const baseErrMsg = chalk.red(
93
- `Failed to automatic download contract ${address} from ${chain}, please manually download abi and put it into abis/eth directory`
94
- )
65
+ const abiRes = await getABI(chain, address, options.name)
66
+ const filename = abiRes.name || address
95
67
 
96
- const filename = options.name ? options.name : address
68
+ writeABIFile(abiRes.abi, getABIFilePath(chain, filename))
97
69
 
98
- if (aptosClient) {
99
- try {
100
- const module = await aptosClient.getAccountModules(address)
101
- writeToDirectory(module, chain, filename)
102
- } catch (e) {
103
- console.error(baseErrMsg, e)
104
- process.exit(1)
105
- }
70
+ // Write contract info to sentio.yaml
71
+ const yamlDocument: yaml.Document = yaml.parseDocument(fs.readFileSync('sentio.yaml', 'utf8'))
72
+ let contracts = yamlDocument.get('contracts') as yaml.YAMLSeq
73
+ if (!contracts) {
74
+ contracts = new yaml.YAMLSeq()
75
+ yamlDocument.set('contracts', contracts)
106
76
  }
107
- if (suiClient) {
108
- try {
109
- const module = await suiClient.getNormalizedMoveModulesByPackage(address)
110
- writeToDirectory(module, chain, filename)
111
- } catch (e) {
112
- console.error(baseErrMsg, e)
113
- process.exit(1)
77
+
78
+ let hasContract = false
79
+ for (const item of contracts.items as yaml.YAMLMap[]) {
80
+ if (item.get('chain') === chain && item.get('address') === address) {
81
+ hasContract = true
114
82
  }
115
83
  }
116
84
 
117
- if (ethApi) {
118
- try {
119
- const resp = await ethApi.contract.getabi(address)
120
- if (resp.status !== '1') {
121
- throw Error(resp.message)
122
- }
123
- writeToDirectory(resp.result, chain, filename)
124
- } catch (e) {
125
- console.error(baseErrMsg, e)
126
- process.exit(1)
85
+ if (!hasContract) {
86
+ const newContract = new yaml.YAMLMap()
87
+ newContract.set('chain', chain)
88
+ newContract.set('address', address)
89
+ if (address !== filename) {
90
+ newContract.set('name', filename)
127
91
  }
92
+ contracts.add(newContract)
93
+ fs.writeFileSync('sentio.yaml', yamlDocument.toString(), 'utf8')
128
94
  }
129
- // Run gen
130
- await codegen()
131
- }
132
- }
133
95
 
134
- function writeToDirectory(obj: object | string, chain: string, name: string) {
135
- if (typeof obj === 'string') {
136
- obj = JSON.parse(obj)
137
- }
138
- const data = JSON.stringify(obj, null, 2)
139
- let subpath = chain
140
- switch (chain) {
141
- case 'aptos':
142
- case 'aptos/testnet':
143
- case 'sui':
144
- case 'sui/testnet':
145
- break
146
- default:
147
- subpath = 'eth'
96
+ // Run gen
97
+ await codegen(false)
148
98
  }
149
-
150
- const output = path.join('abis', subpath, name + '.json')
151
- fs.mkdirSync(path.dirname(output), { recursive: true })
152
- fs.writeFileSync(output, data)
153
- console.log(chalk.green('ABI has been downloaded to', output))
154
99
  }