fix: распаковать источник из обёртки smart-updown в корень ветки
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
# RPC эндпоинт кластера (localnet = запущенный solana-test-validator)
|
||||
SMART_RPC_URL=http://127.0.0.1:8899
|
||||
|
||||
# Разрешить airDrop на localnet (bettor'ам и ваулту). На mainnet выключить.
|
||||
SMART_ALLOW_AIRDROP=1
|
||||
|
||||
# Путь к keypair админа/релеера (тот же ключ = admin и relayer)
|
||||
SMART_ADMIN_KEYPAIR=~/.config/solana/id.json
|
||||
|
||||
# Параметры контракта (должны совпадать со значениями при initialize)
|
||||
SMART_MIN_AMOUNT=100000000 # 0.1 SOL в лампортах
|
||||
SMART_MAX_AMOUNT=10000000000 # 10 SOL в лампортах
|
||||
SMART_MULTIPLIER_BPS=13000 # 1.3x выплата в базисных пунктах
|
||||
SMART_EXPIRY_SECONDS=15 # экспирация ставки (секунды)
|
||||
@@ -0,0 +1,546 @@
|
||||
{
|
||||
"address": "E7Hg55ywRDKRZJ35rDMJGC3YFThQbh6nAK9GDayG4sr3",
|
||||
"metadata": {
|
||||
"name": "smart_updown",
|
||||
"version": "0.1.0",
|
||||
"spec": "0.1.0",
|
||||
"description": "Created with Anchor"
|
||||
},
|
||||
"instructions": [
|
||||
{
|
||||
"name": "close_bet",
|
||||
"discriminator": [
|
||||
2
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "relayer",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "bet",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
98,
|
||||
101,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "arg",
|
||||
"path": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bettor",
|
||||
"docs": [
|
||||
"CHECK: recipient must be the bet's bettor and is validated in the handler."
|
||||
],
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"name": "clock",
|
||||
"address": "SysvarC1ock11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "exit_price",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "create_bet",
|
||||
"discriminator": [
|
||||
1
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "bettor",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bet",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
98,
|
||||
101,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "arg",
|
||||
"path": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clock",
|
||||
"address": "SysvarC1ock11111111111111111111111111111111"
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "side",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "entry_price",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "initialize",
|
||||
"discriminator": [
|
||||
0
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "admin",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "min_amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "max_amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "multiplier_bps",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "expiry_seconds",
|
||||
"type": "i64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "update",
|
||||
"discriminator": [
|
||||
3
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "admin",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "new_relayer",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "min_amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "max_amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "multiplier_bps",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "paused",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "expiry_seconds",
|
||||
"type": "i64"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "Bet",
|
||||
"discriminator": [
|
||||
147,
|
||||
23,
|
||||
35,
|
||||
59,
|
||||
15,
|
||||
75,
|
||||
155,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Global",
|
||||
"discriminator": [
|
||||
167,
|
||||
232,
|
||||
232,
|
||||
177,
|
||||
200,
|
||||
108,
|
||||
114,
|
||||
127
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "BetClosed",
|
||||
"discriminator": [
|
||||
122,
|
||||
20,
|
||||
157,
|
||||
254,
|
||||
221,
|
||||
0,
|
||||
39,
|
||||
129
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BetCreated",
|
||||
"discriminator": [
|
||||
32,
|
||||
153,
|
||||
105,
|
||||
71,
|
||||
188,
|
||||
72,
|
||||
107,
|
||||
114
|
||||
]
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
{
|
||||
"code": 6000,
|
||||
"name": "Paused",
|
||||
"msg": "Operation is paused"
|
||||
},
|
||||
{
|
||||
"code": 6001,
|
||||
"name": "NotAdmin",
|
||||
"msg": "Only the admin can perform this action"
|
||||
},
|
||||
{
|
||||
"code": 6002,
|
||||
"name": "NotRelayer",
|
||||
"msg": "Only the relayer is allowed to close bets"
|
||||
},
|
||||
{
|
||||
"code": 6003,
|
||||
"name": "AmountBelowMin",
|
||||
"msg": "Amount below the allowed minimum"
|
||||
},
|
||||
{
|
||||
"code": 6004,
|
||||
"name": "AmountAboveMax",
|
||||
"msg": "Amount above the allowed maximum"
|
||||
},
|
||||
{
|
||||
"code": 6005,
|
||||
"name": "InvalidSide",
|
||||
"msg": "Invalid side value"
|
||||
},
|
||||
{
|
||||
"code": 6006,
|
||||
"name": "BetAlreadyFinalized",
|
||||
"msg": "Bet is already finalized"
|
||||
},
|
||||
{
|
||||
"code": 6007,
|
||||
"name": "NotExpired",
|
||||
"msg": "Bet cannot be closed before expiry"
|
||||
},
|
||||
{
|
||||
"code": 6008,
|
||||
"name": "BetIdMismatch",
|
||||
"msg": "Bet id mismatch"
|
||||
},
|
||||
{
|
||||
"code": 6009,
|
||||
"name": "InsufficientVault",
|
||||
"msg": "Payout exceeds vault solvency"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"name": "Bet",
|
||||
"serialization": "bytemuck",
|
||||
"repr": {
|
||||
"kind": "c"
|
||||
},
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "bet_time",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "expire_time",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "entry_price",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "exit_price",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "side",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "bettor",
|
||||
"type": "pubkey"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "BetClosed",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "bet",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "exit_price",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "user_won",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "payout",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "BetCreated",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "bet",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "bettor",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "side",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "bet_time",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "expire_time",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "entry_price",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Global",
|
||||
"serialization": "bytemuck",
|
||||
"repr": {
|
||||
"kind": "c"
|
||||
},
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "counter",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "min_amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "max_amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "multiplier_bps",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "expiry_seconds",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "paused",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "relayer",
|
||||
"type": "pubkey"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"name": "BET_SEED",
|
||||
"type": "bytes",
|
||||
"value": "[98, 101, 116]"
|
||||
},
|
||||
{
|
||||
"name": "GLOBAL_SEED",
|
||||
"type": "bytes",
|
||||
"value": "[103, 108, 111, 98, 97, 108]"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
/**
|
||||
* Program IDL in camelCase format in order to be used in JS/TS.
|
||||
*
|
||||
* Note that this is only a type helper and is not the actual IDL. The original
|
||||
* IDL can be found at `target/idl/smart_updown.json`.
|
||||
*/
|
||||
export type SmartUpdown = {
|
||||
"address": "E7Hg55ywRDKRZJ35rDMJGC3YFThQbh6nAK9GDayG4sr3",
|
||||
"metadata": {
|
||||
"name": "smartUpdown",
|
||||
"version": "0.1.0",
|
||||
"spec": "0.1.0",
|
||||
"description": "Created with Anchor"
|
||||
},
|
||||
"instructions": [
|
||||
{
|
||||
"name": "closeBet",
|
||||
"discriminator": [
|
||||
2
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "relayer",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "bet",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
98,
|
||||
101,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "arg",
|
||||
"path": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bettor",
|
||||
"docs": [
|
||||
"CHECK: recipient must be the bet's bettor and is validated in the handler."
|
||||
],
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"name": "clock",
|
||||
"address": "SysvarC1ock11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "exitPrice",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "createBet",
|
||||
"discriminator": [
|
||||
1
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "bettor",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bet",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
98,
|
||||
101,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "arg",
|
||||
"path": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clock",
|
||||
"address": "SysvarC1ock11111111111111111111111111111111"
|
||||
},
|
||||
{
|
||||
"name": "systemProgram",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "side",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "entryPrice",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "initialize",
|
||||
"discriminator": [
|
||||
0
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "admin",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "systemProgram",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "minAmount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "multiplierBps",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "expirySeconds",
|
||||
"type": "i64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "update",
|
||||
"discriminator": [
|
||||
3
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "admin",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "newRelayer",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "minAmount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "multiplierBps",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "paused",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "expirySeconds",
|
||||
"type": "i64"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "bet",
|
||||
"discriminator": [
|
||||
147,
|
||||
23,
|
||||
35,
|
||||
59,
|
||||
15,
|
||||
75,
|
||||
155,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"discriminator": [
|
||||
167,
|
||||
232,
|
||||
232,
|
||||
177,
|
||||
200,
|
||||
108,
|
||||
114,
|
||||
127
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "betClosed",
|
||||
"discriminator": [
|
||||
122,
|
||||
20,
|
||||
157,
|
||||
254,
|
||||
221,
|
||||
0,
|
||||
39,
|
||||
129
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "betCreated",
|
||||
"discriminator": [
|
||||
32,
|
||||
153,
|
||||
105,
|
||||
71,
|
||||
188,
|
||||
72,
|
||||
107,
|
||||
114
|
||||
]
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
{
|
||||
"code": 6000,
|
||||
"name": "paused",
|
||||
"msg": "Operation is paused"
|
||||
},
|
||||
{
|
||||
"code": 6001,
|
||||
"name": "notAdmin",
|
||||
"msg": "Only the admin can perform this action"
|
||||
},
|
||||
{
|
||||
"code": 6002,
|
||||
"name": "notRelayer",
|
||||
"msg": "Only the relayer is allowed to close bets"
|
||||
},
|
||||
{
|
||||
"code": 6003,
|
||||
"name": "amountBelowMin",
|
||||
"msg": "Amount below the allowed minimum"
|
||||
},
|
||||
{
|
||||
"code": 6004,
|
||||
"name": "amountAboveMax",
|
||||
"msg": "Amount above the allowed maximum"
|
||||
},
|
||||
{
|
||||
"code": 6005,
|
||||
"name": "invalidSide",
|
||||
"msg": "Invalid side value"
|
||||
},
|
||||
{
|
||||
"code": 6006,
|
||||
"name": "betAlreadyFinalized",
|
||||
"msg": "Bet is already finalized"
|
||||
},
|
||||
{
|
||||
"code": 6007,
|
||||
"name": "notExpired",
|
||||
"msg": "Bet cannot be closed before expiry"
|
||||
},
|
||||
{
|
||||
"code": 6008,
|
||||
"name": "betIdMismatch",
|
||||
"msg": "Bet id mismatch"
|
||||
},
|
||||
{
|
||||
"code": 6009,
|
||||
"name": "insufficientVault",
|
||||
"msg": "Payout exceeds vault solvency"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"name": "bet",
|
||||
"serialization": "bytemuck",
|
||||
"repr": {
|
||||
"kind": "c"
|
||||
},
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "betTime",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "expireTime",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "entryPrice",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "exitPrice",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "side",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "bettor",
|
||||
"type": "pubkey"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "betClosed",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "bet",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "exitPrice",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "userWon",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "payout",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "betCreated",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "bet",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "bettor",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "side",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "betTime",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "expireTime",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "entryPrice",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "global",
|
||||
"serialization": "bytemuck",
|
||||
"repr": {
|
||||
"kind": "c"
|
||||
},
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "counter",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "minAmount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "multiplierBps",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "expirySeconds",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "paused",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "relayer",
|
||||
"type": "pubkey"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"name": "betSeed",
|
||||
"type": "bytes",
|
||||
"value": "[98, 101, 116]"
|
||||
},
|
||||
{
|
||||
"name": "globalSeed",
|
||||
"type": "bytes",
|
||||
"value": "[103, 108, 111, 98, 97, 108]"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "smart-updown-relayer",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"init": "ts-node src/index.ts --init --once",
|
||||
"run": "ts-node src/index.ts --once",
|
||||
"loop": "ts-node src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anchor-lang/core": "^1.0.0",
|
||||
"@solana/web3.js": "^1.98.0",
|
||||
"bn.js": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^5.2.0",
|
||||
"@types/node": "^25.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Connection, clusterApiUrl, Keypair, PublicKey } from "@solana/web3.js";
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
export const SIDE_UP = 0;
|
||||
export const SIDE_DOWN = 1;
|
||||
|
||||
/**
|
||||
* Runtime configuration. Everything can be overridden via environment
|
||||
* variables; defaults target the local validator.
|
||||
*/
|
||||
export interface Config {
|
||||
/** RPC endpoint of the cluster (localnet by default). */
|
||||
rpcUrl: string;
|
||||
/** Localnet allows naive airdrops (to bettors and the house vault). */
|
||||
allowAirdrop: boolean;
|
||||
/** Program id. Defaults to the value embedded in the IDL. */
|
||||
programId: PublicKey;
|
||||
/** House admin/relayer keypair file path. */
|
||||
adminKeypairPath: string;
|
||||
/** Initial parameters used by `init` (must match on-chain constants). */
|
||||
minAmountLamports: number;
|
||||
maxAmountLamports: number;
|
||||
multiplierBps: number;
|
||||
expirySeconds: number;
|
||||
}
|
||||
|
||||
function env(name: string, fallback: string): string {
|
||||
return process.env[name] ?? fallback;
|
||||
}
|
||||
|
||||
function envNum(name: string, fallback: number): number {
|
||||
const raw = process.env[name];
|
||||
if (!raw) return fallback;
|
||||
const n = Number(raw);
|
||||
if (!Number.isFinite(n)) throw new Error(`Invalid number for env ${name}: ${raw}`);
|
||||
return n;
|
||||
}
|
||||
|
||||
export function loadConfig(): Config {
|
||||
const rpcUrl = env("SMART_RPC_URL", "http://127.0.0.1:8899");
|
||||
const allowAirdrop = env("SMART_ALLOW_AIRDROP", "1") === "1";
|
||||
const adminKeypairPath =
|
||||
env("SMART_ADMIN_KEYPAIR", path.join(os.homedir(), ".config/solana/id.json"));
|
||||
|
||||
return {
|
||||
rpcUrl,
|
||||
allowAirdrop,
|
||||
programId: PublicKey.default, // placeholder; real value read from the IDL below
|
||||
adminKeypairPath,
|
||||
minAmountLamports: envNum("SMART_MIN_AMOUNT", 100_000_000), // 0.1 SOL
|
||||
maxAmountLamports: envNum("SMART_MAX_AMOUNT", 10_000_000_000), // 10 SOL
|
||||
multiplierBps: envNum("SMART_MULTIPLIER_BPS", 13_000), // 1.3x
|
||||
expirySeconds: envNum("SMART_EXPIRY_SECONDS", 15),
|
||||
};
|
||||
}
|
||||
|
||||
export function loadKeypair(pathOrJson: string): Keypair {
|
||||
const resolved = pathOrJson.startsWith("~")
|
||||
? path.join(os.homedir(), pathOrJson.slice(1))
|
||||
: path.resolve(pathOrJson);
|
||||
const raw = fs.readFileSync(resolved, "utf8");
|
||||
return Keypair.fromSecretKey(Uint8Array.from(JSON.parse(raw)));
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Keypair } from "@solana/web3.js";
|
||||
import { SIDE_DOWN, SIDE_UP } from "./config";
|
||||
|
||||
/**
|
||||
* STUB — «фронт»: от пользователя приходят параметры ставки.
|
||||
*
|
||||
* В реальной системе это сообщение присылает фронтенд (или твоя БД) после
|
||||
* того, как пользователь выбрал направление и сумму. Здесь мы эмулируем
|
||||
* входящую очередь: случайный UP/DOWN и случайную сумму в заданном диапазоне.
|
||||
*/
|
||||
export interface BetRequest {
|
||||
bettor: Keypair;
|
||||
side: number; // SIDE_UP | SIDE_DOWN
|
||||
amountLamports: number;
|
||||
}
|
||||
|
||||
/** Очередь входящих ставок. В заглушке наполняем случайными значениями. */
|
||||
export function nextBetRequest(minAmount: number, maxAmount: number): BetRequest {
|
||||
const bettor = Keypair.generate();
|
||||
const side = Math.random() < 0.5 ? SIDE_UP : SIDE_DOWN;
|
||||
const amount =
|
||||
minAmount + Math.floor(Math.random() * (maxAmount - minAmount + 1));
|
||||
return { bettor, side, amountLamports: amount };
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
|
||||
import { Program } from "@anchor-lang/core";
|
||||
import { loadConfig, loadKeypair, SIDE_UP } from "./config";
|
||||
import { getPrice } from "./priceSource";
|
||||
import { nextBetRequest } from "./frontStub";
|
||||
import {
|
||||
initialize,
|
||||
createBet,
|
||||
closeBet,
|
||||
readGlobal,
|
||||
ensureVaultFunded,
|
||||
} from "./ops";
|
||||
import { betPda } from "./pda";
|
||||
import { makeConnection, makeProgram } from "./program";
|
||||
|
||||
import type { SmartUpdown } from "../idl/smart_updown";
|
||||
|
||||
const LAMPORTS_PER_SOL_NUM = Number(LAMPORTS_PER_SOL);
|
||||
|
||||
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
const STATUS_OPEN = 0;
|
||||
async function fundBettor(connection: any, bettorPubkey: PublicKey): Promise<void> {
|
||||
const bal = await connection.getBalance(bettorPubkey);
|
||||
if (bal < 2 * LAMPORTS_PER_SOL_NUM) {
|
||||
await connection.requestAirdrop(bettorPubkey, 5 * LAMPORTS_PER_SOL_NUM);
|
||||
await sleep(800);
|
||||
console.log(`bettor funded ${bettorPubkey.toBase58()} (+5 SOL)`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Закрывает ставку, повторяя при «equal price» (exit==entry) пока не зафиналится. */
|
||||
async function settleBet(
|
||||
program: Program<SmartUpdown>,
|
||||
id: any,
|
||||
entryPrice: number,
|
||||
relayer: any,
|
||||
bettor: PublicKey
|
||||
): Promise<void> {
|
||||
for (let attempt = 1; ; attempt++) {
|
||||
const exitPrice = await getPrice();
|
||||
if (exitPrice === entryPrice) {
|
||||
console.log(`attempt ${attempt}: exit==entry (${exitPrice}), жду движение...`);
|
||||
await sleep(2000);
|
||||
continue;
|
||||
}
|
||||
const sig = await closeBet(program, id, exitPrice, relayer, bettor);
|
||||
console.log(`close_bet #${id} exit=${exitPrice} (attempt ${attempt}) tx=${sig}`);
|
||||
|
||||
const status = await getStatus(program, id);
|
||||
if (status === STATUS_OPEN) {
|
||||
console.log(`ставка ещё open (equal price), повторяю close...`);
|
||||
await sleep(2000);
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function getStatus(program: Program<SmartUpdown>, id: any): Promise<number> {
|
||||
const bet = await program.account.bet.fetchNullable(betPda(program.programId, id));
|
||||
return bet ? bet.status.toNumber() : -1;
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const cfg = loadConfig();
|
||||
const admin = loadKeypair(cfg.adminKeypairPath);
|
||||
const connection = makeConnection(cfg.rpcUrl);
|
||||
const program = makeProgram(connection, admin);
|
||||
|
||||
console.log(`RPC: ${cfg.rpcUrl}`);
|
||||
console.log(`Admin/relayer: ${admin.publicKey.toBase58()}`);
|
||||
|
||||
const global = await readGlobal(program, program.programId);
|
||||
if (!global) {
|
||||
if (!process.argv.includes("--init")) {
|
||||
throw new Error(
|
||||
"Контракт не инициализирован. Запусти с флагом --init (или init+run)."
|
||||
);
|
||||
}
|
||||
await initialize(program, {
|
||||
minAmount: cfg.minAmountLamports,
|
||||
maxAmount: cfg.maxAmountLamports,
|
||||
multiplierBps: cfg.multiplierBps,
|
||||
expirySeconds: cfg.expirySeconds,
|
||||
}, admin);
|
||||
console.log("Контракт инициализирован.");
|
||||
} else {
|
||||
console.log(
|
||||
`Global: counter=${global.counter} vault=${(global.lamports / LAMPORTS_PER_SOL_NUM).toFixed(2)} SOL ` +
|
||||
`expiry=${global.expirySeconds}s paused=${global.paused}`
|
||||
);
|
||||
}
|
||||
|
||||
if (cfg.allowAirdrop) {
|
||||
await ensureVaultFunded(connection, program, 10 * LAMPORTS_PER_SOL_NUM);
|
||||
}
|
||||
|
||||
const loops = process.argv.includes("--once") ? 1 : Infinity;
|
||||
let n = 0;
|
||||
|
||||
while (n < loops) {
|
||||
n++;
|
||||
const req = nextBetRequest(cfg.minAmountLamports, cfg.maxAmountLamports);
|
||||
await fundBettor(connection, req.bettor.publicKey);
|
||||
|
||||
const entryPrice = await getPrice();
|
||||
await createBet(program, req.bettor, {
|
||||
side: req.side,
|
||||
amount: req.amountLamports,
|
||||
entryPrice,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`ставка ${req.side === SIDE_UP ? "UP" : "DOWN"} на ${(req.amountLamports / LAMPORTS_PER_SOL_NUM).toFixed(3)} SOL, жду ${cfg.expirySeconds}s...`
|
||||
);
|
||||
await sleep((cfg.expirySeconds + 1) * 1000);
|
||||
|
||||
const g0 = await readGlobal(program, program.programId);
|
||||
const id = g0!.counter.subn(1);
|
||||
await settleBet(program, id, entryPrice, admin, req.bettor.publicKey);
|
||||
|
||||
const outcome = await program.account.bet.fetchNullable(betPda(program.programId, id));
|
||||
console.log(
|
||||
outcome!.status.toNumber() === 1
|
||||
? `>> результат #${id}: ПОЛЬЗОВАТЕЛЬ ВЫИГРАЛ (payout 1.3x)`
|
||||
: `>> результат #${id}: ВЫИГРАЛ ДОМ (user lost)`
|
||||
);
|
||||
}
|
||||
console.log("Готово.");
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import { Program } from "@anchor-lang/core";
|
||||
import { SIDE_UP } from "./config";
|
||||
import { globalPda } from "./pda";
|
||||
|
||||
import type { SmartUpdown } from "../idl/smart_updown";
|
||||
|
||||
const LAMPORTS_PER_SOL_NUM = Number(LAMPORTS_PER_SOL);
|
||||
|
||||
/** Считывает account Global (ваулт). Возвращает null, если ещё не инициализирован. */
|
||||
export async function readGlobal(
|
||||
program: Program<SmartUpdown>,
|
||||
programId: PublicKey
|
||||
): Promise<{
|
||||
counter: BN;
|
||||
minAmount: BN;
|
||||
maxAmount: BN;
|
||||
multiplierBps: BN;
|
||||
expirySeconds: BN;
|
||||
paused: BN;
|
||||
admin: PublicKey;
|
||||
relayer: PublicKey;
|
||||
lamports: number;
|
||||
} | null> {
|
||||
const global = globalPda(programId);
|
||||
const raw = await program.account.global.fetchNullable(global);
|
||||
if (!raw) return null;
|
||||
const lamports = await program.provider.connection.getBalance(global);
|
||||
return {
|
||||
counter: raw.counter,
|
||||
minAmount: raw.minAmount,
|
||||
maxAmount: raw.maxAmount,
|
||||
multiplierBps: raw.multiplierBps,
|
||||
expirySeconds: raw.expirySeconds,
|
||||
paused: raw.paused,
|
||||
admin: raw.admin,
|
||||
relayer: raw.relayer,
|
||||
lamports,
|
||||
};
|
||||
}
|
||||
|
||||
/** Одноразовая инициализация контракта (вызывается admin'ом до приёма ставок). */
|
||||
export async function initialize(
|
||||
program: Program<SmartUpdown>,
|
||||
args: {
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
multiplierBps: number;
|
||||
expirySeconds: number;
|
||||
},
|
||||
admin: Keypair
|
||||
): Promise<string> {
|
||||
const sig = await program.methods
|
||||
.initialize(
|
||||
new BN(args.minAmount),
|
||||
new BN(args.maxAmount),
|
||||
new BN(args.multiplierBps),
|
||||
new BN(args.expirySeconds)
|
||||
)
|
||||
.accounts({
|
||||
admin: admin.publicKey,
|
||||
})
|
||||
.signers([admin])
|
||||
.rpc();
|
||||
return sig;
|
||||
}
|
||||
|
||||
/** Создаёт ставку от имени пользователя. Возвращает фактический id (== counter). */
|
||||
export async function createBet(
|
||||
program: Program<SmartUpdown>,
|
||||
bettor: Keypair,
|
||||
args: { side: number; amount: number; entryPrice: number }
|
||||
): Promise<BN> {
|
||||
const global = await readGlobal(program, program.programId);
|
||||
if (!global) throw new Error("Contract not initialized");
|
||||
const id = global.counter;
|
||||
|
||||
const sig = await program.methods
|
||||
.createBet(new BN(id), new BN(args.side), new BN(args.amount), new BN(args.entryPrice))
|
||||
.accounts({
|
||||
bettor: bettor.publicKey,
|
||||
})
|
||||
.signers([bettor])
|
||||
.rpc();
|
||||
|
||||
console.log(`create_bet #${id} side=${args.side === SIDE_UP ? "UP" : "DOWN"} ` +
|
||||
`amount=${(args.amount / LAMPORTS_PER_SOL_NUM).toFixed(3)} SOL entry=${args.entryPrice} tx=${sig}`);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Закрывает ставку после экспирации.
|
||||
* Если exit_price == entry_price, контракт не финализирует ставку — тогда
|
||||
* ждём первое движение цены и повторяем close (см. обработку в main loop).
|
||||
*/
|
||||
export async function closeBet(
|
||||
program: Program<SmartUpdown>,
|
||||
id: BN,
|
||||
exitPrice: number,
|
||||
relayer: Keypair,
|
||||
bettor: PublicKey
|
||||
): Promise<string> {
|
||||
return program.methods
|
||||
.closeBet(new BN(id), new BN(exitPrice))
|
||||
.accounts({
|
||||
relayer: relayer.publicKey,
|
||||
bettor,
|
||||
})
|
||||
.signers([relayer])
|
||||
.rpc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Догружает ваулт (house bankroll), чтобы хватило на выплаты 1.3x.
|
||||
* На localnet используем airDrop на PDA ваулта; на mainnet воздух не накапает —
|
||||
* там нужно переводить SOL с админа (off-chain, вне этого CLI).
|
||||
*/
|
||||
export async function ensureVaultFunded(
|
||||
connection: Connection,
|
||||
program: Program<SmartUpdown>,
|
||||
amountLamports: number
|
||||
): Promise<void> {
|
||||
const global = globalPda(program.programId);
|
||||
const current = await connection.getBalance(global);
|
||||
const needed = amountLamports; // будущая выплата (до 1.3x), запасаем с запасом
|
||||
if (current >= needed) return;
|
||||
const topUp = needed - current + 5 * LAMPORTS_PER_SOL_NUM; // +5 SOL запаса
|
||||
await connection.requestAirdrop(global, topUp);
|
||||
console.log(`vault funded +${(topUp / LAMPORTS_PER_SOL_NUM).toFixed(2)} SOL`);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
|
||||
/** Seeds, согласованные с контрактом (см. constants.rs). */
|
||||
export const GLOBAL_SEED = Buffer.from("global");
|
||||
export const BET_SEED = Buffer.from("bet");
|
||||
|
||||
export function globalPda(programId: PublicKey): PublicKey {
|
||||
return PublicKey.findProgramAddressSync([GLOBAL_SEED], programId)[0];
|
||||
}
|
||||
|
||||
export function betPda(programId: PublicKey, id: BN): PublicKey {
|
||||
const idLE = Buffer.alloc(8);
|
||||
idLE.writeBigUInt64LE(BigInt(id.toString(10)));
|
||||
return PublicKey.findProgramAddressSync([BET_SEED, idLE], programId)[0];
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* STUB — SOL price source.
|
||||
*
|
||||
* TODO(пользователь): замени `getPrice` на реальный запрос к своей БД.
|
||||
* Договоримся, что функция возвращает цену SOL как целое число в «центах»
|
||||
* (то же юнит-пространство, что у веса entry/exit на контракте). Контракт
|
||||
* сравнивает тол ько «больше/меньше/равно», поэтому само юнит-пространство
|
||||
* не критично — главное, чтобы оно было согласовано между entry и exit.
|
||||
*/
|
||||
let simulated = 100_000_000;
|
||||
|
||||
/** Цена SOL сейчас (заглушка — детерминированно «гуляет»). */
|
||||
export async function getPrice(): Promise<number> {
|
||||
const drift = Math.round(Math.random() * 200_000); // ±0.2% движка
|
||||
simulated += Math.random() < 0.5 ? drift : -drift;
|
||||
return simulated;
|
||||
}
|
||||
|
||||
/** Управляемая заглушка: вернуть ровно `value` (полезна для теста «equal price»). */
|
||||
export function setPriceOverride(value: number | undefined): void {
|
||||
simulated = value ?? 100_000_000;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { AnchorProvider, Program, Wallet } from "@anchor-lang/core";
|
||||
import { Keypair } from "@solana/web3.js";
|
||||
import idl from "../idl/smart_updown.json";
|
||||
|
||||
import type { SmartUpdown } from "../idl/smart_updown";
|
||||
|
||||
/**
|
||||
* Создаёт готовый клиент программы: Connection + AnchorProvider (подпись админа-
|
||||
* релеера) + типизированный Program, собранный из IDL.
|
||||
*/
|
||||
export function makeProgram(
|
||||
connection: Connection,
|
||||
admin: Keypair
|
||||
): Program<SmartUpdown> {
|
||||
const provider = new AnchorProvider(
|
||||
connection,
|
||||
new Wallet(admin),
|
||||
{ commitment: "confirmed", preflightCommitment: "confirmed" }
|
||||
);
|
||||
// IDL.address задаёт programId в рантайме.
|
||||
return new Program<SmartUpdown>(idl as unknown as SmartUpdown, provider);
|
||||
}
|
||||
|
||||
export function makeConnection(rpcUrl: string): Connection {
|
||||
return new Connection(rpcUrl, "confirmed");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["es2020"],
|
||||
"module": "commonjs",
|
||||
"target": "es2020",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "idl/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user