forked from EmelyanenkoK/basecoin
-
Notifications
You must be signed in to change notification settings - Fork 4
/
claimApi.ts
163 lines (136 loc) · 7.09 KB
/
claimApi.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import { Address, beginCell, BitBuilder, Cell, Dictionary, DictionaryValue, exoticMerkleProof, exoticPruned, toNano, internal, storeStateInit } from '@ton/core';
import {compile, NetworkProvider} from '@ton/blueprint';
//import { MnemonicProvider } from '@ton/blueprint';
import {jettonWalletCodeFromLibrary, promptUrl, promptUserFriendlyAddress} from "../wrappers/ui-utils";
import { WalletContractV3R2, WalletContractV4, WalletContractV5R1 } from "@ton/ton";
import { mnemonicNew, mnemonicToPrivateKey } from "@ton/crypto";
import '@ton/test-utils';
import {jettonContentToCell, JettonMinter} from '../wrappers/JettonMinter';
import { JettonWallet, jettonWalletConfigToCell } from '../wrappers/JettonWallet';
import { buff2bigint} from '../sandbox_tests/utils';
import { Errors, Op } from '../wrappers/JettonConstants';
declare module 'express';
/*
We need to write simple server
it exposes method `/wallet/:address`:
`/wallet/:address`
`address` is raw encoded addr_std of jetton_wallet owner (NOT jetton_wallet address). (example: `0:1234567890absdef1234567890absdef1234567890absdef1234567890absdef`)
Returns full information about a particular wallet including the `CustomPayload` that should be attchaed to transfer.
MUST return a JSON object with the following fields:
| Name | Type | Description |
|----------------|--------|--------------------------------------------------------------------------------------------------|
| owner | string | wallet owner address in raw form |
| jetton_wallet | string | jetton_wallet address in raw form TODO: maybe should be removed to avoid incositency in API |
| custom_payload | string | Custom payload which wallet MUST attach to transfer message. Serialized as base64 BoC |
MUST return some field in specific cases:
| Name | Type | Case | Description |
|-----------------|--------|-------------------------|-----------------------------------------------------------------------------|
| state_init | string | claim compressed jetton | State init SHOULD be attached to transfer message. Serialized as base64 BoC |
| compressed_info | object | claim compressed jetton | |
Other fields MAY be returned.
Example:
```json
{
"owner": "0:0000000000000000000000000000000000000000000000000000000000000000",
"jetton_wallet": "0:1234567890absdef1234567890absdef1234567890absdef1234567890absdef",
"custom_payload": "te6cckEBBgEAeQACAAEDAUOAFa1KqA2Oswxbo4Rgh/q6NEaPLuK9o3fo1TFGn+MySjqQAgAMMi5qc29uAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBQF4S3GMDJY/HoZd6TCREIOnCaYlF23hNzJaSsfMd1S7nBQAA8muEeQ==",
"state_init": "te6ccgECGAEABLAAAgE0AgEBhwgBm5yw6LpMut8/oVy48oR5MjqgHd8X80GPKq12i/8q260APgFPbn4IpB/3t4HNXmxz7Qp2VJj46hROuO+ZFb+FyIegAgEU/wD0pBP0vPLICwMCAWIEBQICzAYHABug9gXaiaH0AfSB9IGoYQIB1AgJAgEgCgsAwwgxwCSXwTgAdDTAwFxsJUTXwPwDOD6QPpAMfoAMXHXIfoAMfoAMHOptAAC0x+CEA+KfqVSILqVMTRZ8AngghAXjUUZUiC6ljFERAPwCuA1ghBZXwe8upNZ8AvgXwSED/LwgABE+kQwcLry4U2ACASAMDQCD1AEGuQ9qJofQB9IH0gahgCaY/BCAvGooypEF1BCD3uy+8J3QlY+XFi6Z+Y/QAYCdAoEeQoAn0BLGeLAOeLZmT2qkAvVQPTP/oA+kAh8AHtRND6APpA+kDUMFE2oVIqxwXy4sEowv/y4sJUdAJwVCATVBQDyFAE+gJYzxYBzxbMySLIywES9AD0AMsAySD5AHB0yMsCygfL/8nQBfpA9AQx+gAg10nCAPLixCD0BD=",
"compressed_info": {
"amount": "1000000000",
"start_from": "1673808578",
"expired_at": "1721080197"
}
}
```
*/
// on start this server reads file airdropData.boc
type AirdropData = {
amount: bigint,
start_from: number,
expire_at: number
};
const airDropValue: DictionaryValue<AirdropData> = {
serialize: (src, builder) => {
builder.storeCoins(src.amount);
builder.storeUint(src.start_from, 48);
builder.storeUint(src.expire_at, 48);
},
parse: (src) => {
return {
amount: src.loadCoins(),
start_from: src.loadUint(48),
expire_at: src.loadUint(48)
}
}
}
// airdropData.boc contains serialized airdrop dictionary
// read file as Buffer
const fs = require('fs');
const airdropDataBuffer = fs.readFileSync('airdropData.boc');
// parse buffer to cell
const airdropCell = Cell.fromBoc(airdropDataBuffer)[0];
let airdropData = Dictionary.loadDirect(Dictionary.Keys.Address(), airDropValue, airdropCell);
let merkleRoot = buff2bigint(airdropCell.hash(0));
//minter address is stored in file minter.json
const minterBuffer = fs.readFileSync('minter.json');
//parse json
const minterData = JSON.parse(minterBuffer.toString());
const minter = Address.parse(minterData.address);
let wallet_code_raw = beginCell().endCell();
(async () => { wallet_code_raw = await compile('JettonWallet'); })();
const wallet_code = jettonWalletCodeFromLibrary(wallet_code_raw);
/* then necessary data can be extracted from airdropData as
```
let airdrop = airdropData.get(Address.parse(address as string));
let amount = airdrop!.amount;
let start_from = airdrop!.start_from;
let expire_at = airdrop!.expire_at;
let receiverProof = airdropData.generateMerkleProof(Address.parse(address as string));
//serialize receiverProof: toBoc, then Buffer to base64
let serializedProof = receiverProof.toBoc().toString('base64');
```
*/
// start server
import express from 'express';
import { Request, Response } from 'express';
const app = express();
const port = 3000;
app.get('/wallet/:address', (req: Request, res: Response) => {
let address = req.params.address;
let owner = Address.parse(address as string);
let airdrop = airdropData.get(owner);
if(airdrop === undefined) {
res.json({}); // return empty object if no airdrop data for this address
return;
}
let amount = airdrop!.amount;
let start_from = airdrop!.start_from;
let expire_at = airdrop!.expire_at;
let receiverProof = airdropData.generateMerkleProof(owner);
//serialize receiverProof: toBoc, then Buffer to base64
let serializedProof = receiverProof.toBoc().toString('base64');
const claimPayload = JettonWallet.claimPayload(receiverProof);
let custom_payload = claimPayload.toBoc().toString('base64');
let jettonWalletContract = JettonWallet.createFromConfig({ownerAddress: owner,
jettonMasterAddress: minter,
merkleRoot: merkleRoot,
}, wallet_code);
let stateInitB = beginCell();
storeStateInit(jettonWalletContract.init!)(stateInitB);
let stateInit = stateInitB.endCell();
res.json({
owner: owner.toRawString(),
jetton_wallet: jettonWalletContract.address.toRawString(),
custom_payload,
state_init: stateInit.toBoc().toString('base64'),
compressed_info: {
amount: amount.toString(),
start_from: start_from.toString(),
expired_at: expire_at.toString()
}
});
}
);
app.listen(port, () => {
console.log(`Server started at http://localhost:${port}`);
});