Skip to content

Commit 099e60d

Browse files
authored
Secure mint handle errors (#3906)
* Secure mint handle errors * Rename
1 parent 6d7fb2a commit 099e60d

File tree

8 files changed

+251
-10
lines changed

8 files changed

+251
-10
lines changed

.changeset/short-cheetahs-promise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@chainlink/secure-mint-adapter': patch
3+
---
4+
5+
Handle errors

packages/composites/secure-mint/src/transport/mintable.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,26 @@ export class MintableTransport extends SubscriptionTransport<BaseEndpointTypes>
8282
),
8383
])
8484

85+
const hasSupplyError = Object.values(supply.chains).some((data) => 'error_message' in data)
86+
8587
// We will prevent minting if there is more supply + pre-mint than reserve
86-
const overmint = BigInt(supply.premint) + BigInt(supply.supply) > reserve.reserveAmount
88+
const overmint = hasSupplyError
89+
? false
90+
: BigInt(supply.premint) + BigInt(supply.supply) > reserve.reserveAmount
8791

8892
const data = {
8993
overmint,
90-
mintables: Object.fromEntries(
91-
Object.entries(supply.chains).map(([id, data]) => [
92-
id,
93-
{
94-
mintable: overmint ? '0' : data.mintable,
95-
block: data.response_block,
96-
},
97-
]),
98-
),
94+
mintables: hasSupplyError
95+
? {}
96+
: Object.fromEntries(
97+
Object.entries(supply.chains).map(([id, data]) => [
98+
id,
99+
{
100+
mintable: overmint ? '0' : data.mintable,
101+
block: data.response_block,
102+
},
103+
]),
104+
),
99105
reserveInfo: {
100106
reserveAmount: reserve.reserveAmount.toString(),
101107
timestamp: reserve.timestamp,

packages/composites/secure-mint/src/transport/supply.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { AdapterError } from '@chainlink/external-adapter-framework/validation/e
44
import { BaseEndpointTypes, inputParameters } from '../endpoint/mintable'
55

66
type ChainData = {
7+
error_message: string
78
latest_block: number
89
response_block: number
910
request_block: number

packages/composites/secure-mint/test/integration/__snapshots__/adapter.test.ts.snap

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,36 @@ exports[`execute mintable endpoint should block overmint 1`] = `
4545
}
4646
`;
4747

48+
exports[`execute mintable endpoint should handle error 1`] = `
49+
{
50+
"data": {
51+
"latestBlocks": {
52+
"1": 5,
53+
},
54+
"mintables": {},
55+
"overmint": false,
56+
"reserveInfo": {
57+
"reserveAmount": "1000000000000000000",
58+
"timestamp": 2,
59+
},
60+
"supplyDetails": {
61+
"chains": {
62+
"1": {
63+
"error_message": "some error messages",
64+
"latest_block": 5,
65+
},
66+
},
67+
},
68+
},
69+
"result": 0,
70+
"statusCode": 200,
71+
"timestamps": {
72+
"providerDataReceivedUnixMs": 978347471111,
73+
"providerDataRequestedUnixMs": 978347471111,
74+
},
75+
}
76+
`;
77+
4878
exports[`execute mintable endpoint should return success 1`] = `
4979
{
5080
"data": {

packages/composites/secure-mint/test/integration/adapter.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,21 @@ describe('execute', () => {
6464
expect(response.statusCode).toBe(200)
6565
expect(response.json()).toMatchSnapshot()
6666
})
67+
68+
it('should handle error', async () => {
69+
mockBitgoSuccess()
70+
mockIndexerSuccess()
71+
72+
const data = {
73+
token: 'token3',
74+
reserves: 'Bitgo',
75+
supplyChains: ['1'],
76+
supplyChainBlocks: [0],
77+
}
78+
const response = await testAdapter.request(data)
79+
80+
expect(response.statusCode).toBe(200)
81+
expect(response.json()).toMatchSnapshot()
82+
})
6783
})
6884
})

packages/composites/secure-mint/test/integration/fixtures.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ export const mockBitgoSuccess = (): nock.Scope =>
2020
},
2121
}))
2222
.persist()
23+
.post('/', { data: { client: 'token3' } })
24+
.reply(200, () => ({
25+
result: 1,
26+
timestamps: {
27+
providerDataReceivedUnixMs: 2,
28+
},
29+
}))
30+
.persist()
2331

2432
export const mockIndexerSuccess = (): nock.Scope =>
2533
nock('http://fake-indexer', {
@@ -75,3 +83,18 @@ export const mockIndexerSuccess = (): nock.Scope =>
7583
},
7684
}))
7785
.persist()
86+
.post('/data', {
87+
token: 'token3',
88+
chains: {
89+
'1': 0,
90+
},
91+
})
92+
.reply(200, () => ({
93+
chains: {
94+
'1': {
95+
error_message: 'some error messages',
96+
latest_block: 5,
97+
},
98+
},
99+
}))
100+
.persist()

packages/composites/secure-mint/test/unit/mintable.test.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,5 +455,119 @@ describe('MintableTransport', () => {
455455
)
456456
expect(requester.request).toHaveBeenCalledTimes(2)
457457
})
458+
459+
it('multi chains - one error', async () => {
460+
const reserveResult = 1
461+
const premint = '102'
462+
const supply = '1'
463+
const supplyChain = ['1', '56']
464+
const supplyChainBlock = [107, 108]
465+
466+
const reservesResponse = makeStub('reservesResponse', {
467+
response: {
468+
data: {
469+
result: reserveResult,
470+
timestamps: {
471+
providerDataReceivedUnixMs: Date.now(),
472+
},
473+
},
474+
},
475+
})
476+
477+
requester.request.mockResolvedValueOnce(reservesResponse)
478+
479+
const supplyResponse = makeStub('supplyResponse', {
480+
response: {
481+
data: {
482+
premint,
483+
supply,
484+
chains: {
485+
'1': {
486+
error_message: 'error1',
487+
latest_block: 106,
488+
},
489+
'56': {
490+
mintable: '2104',
491+
response_block: 2105,
492+
latest_block: 2106,
493+
},
494+
},
495+
},
496+
},
497+
})
498+
499+
requester.request.mockResolvedValueOnce(supplyResponse)
500+
501+
const param = makeStub('param', {
502+
token: 'ETH',
503+
reserves: 'Bitgo',
504+
supplyChains: supplyChain,
505+
supplyChainBlocks: supplyChainBlock,
506+
} as typeof inputParameters.validated)
507+
const response = await transport._handleRequest(param)
508+
509+
expect(response).toEqual({
510+
data: {
511+
overmint: false,
512+
latestBlocks: {
513+
1: 106,
514+
56: 2106,
515+
},
516+
mintables: {},
517+
reserveInfo: {
518+
reserveAmount: BigInt(reserveResult * 10 ** 18).toString(),
519+
timestamp: Date.now(),
520+
},
521+
supplyDetails: {
522+
chains: {
523+
'1': {
524+
error_message: 'error1',
525+
latest_block: 106,
526+
},
527+
'56': {
528+
latest_block: 2106,
529+
mintable: '2104',
530+
response_block: 2105,
531+
},
532+
},
533+
premint,
534+
supply,
535+
},
536+
},
537+
result: 0,
538+
statusCode: 200,
539+
timestamps: {
540+
providerDataReceivedUnixMs: Date.now(),
541+
providerDataRequestedUnixMs: Date.now(),
542+
},
543+
})
544+
545+
const expectedReserveRequestConfig = requestConfigForReserve({
546+
token: param.token,
547+
})
548+
const expectedReserveRequestKey = requestKeyForConfig(expectedReserveRequestConfig)
549+
550+
expect(requester.request).toHaveBeenNthCalledWith(
551+
1,
552+
expectedReserveRequestKey,
553+
expectedReserveRequestConfig,
554+
)
555+
556+
const expectedSupplyRequestConfig = requestConfigForSupply({
557+
token: param.token,
558+
chains: {
559+
'1': 107,
560+
'56': 108,
561+
},
562+
})
563+
const expectedSupplyRequestKey = requestKeyForConfig(expectedSupplyRequestConfig)
564+
565+
expect(requester.request).toHaveBeenNthCalledWith(
566+
2,
567+
expectedSupplyRequestKey,
568+
expectedSupplyRequestConfig,
569+
)
570+
expect(requester.request).toHaveBeenCalledTimes(2)
571+
})
458572
})
459573
})

packages/composites/secure-mint/test/unit/supply.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ const requester = makeStub('requester', {
1919
request: jest.fn(),
2020
})
2121

22+
beforeEach(() => {
23+
requester.request.mockReset()
24+
})
25+
2226
describe('getReserve', () => {
2327
it('returns parsed reserveAmount and timestamp', async () => {
2428
const response = {
@@ -85,6 +89,48 @@ describe('getReserve', () => {
8589
expect(result).toStrictEqual(response)
8690
})
8791

92+
it('returns parsed error_message', async () => {
93+
const response = {
94+
chains: {
95+
'1': {
96+
latest_block: 300,
97+
error_message: 'error',
98+
},
99+
},
100+
}
101+
requester.request.mockResolvedValueOnce(
102+
makeStub('reservesResponse', {
103+
response: {
104+
data: response,
105+
},
106+
}),
107+
)
108+
109+
const result = await getSupply(
110+
token,
111+
['1'],
112+
[100],
113+
requester as unknown as Requester,
114+
config,
115+
endpointName,
116+
transportName,
117+
)
118+
119+
expect(requester.request).toHaveBeenNthCalledWith(1, expect.any(String), {
120+
method: 'post',
121+
baseURL: config.SECURE_MINT_INDEXER_URL,
122+
url: 'data',
123+
data: {
124+
token,
125+
chains: {
126+
'1': 100,
127+
},
128+
},
129+
})
130+
131+
expect(result).toStrictEqual(response)
132+
})
133+
88134
it('throws exception - use errorResponse', async () => {
89135
requester.request.mockRejectedValue(
90136
new AdapterError({

0 commit comments

Comments
 (0)