@gobob/bob-sdk 1.3.0 → 2.1.0
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/dist/gateway/client.d.ts +56 -0
- package/dist/gateway/client.js +160 -0
- package/dist/gateway/client.js.map +1 -0
- package/dist/gateway/index.d.ts +2 -0
- package/dist/gateway/index.js +6 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/tokens.d.ts +12 -0
- package/dist/gateway/tokens.js +64 -0
- package/dist/gateway/tokens.js.map +1 -0
- package/dist/gateway/types.d.ts +18 -0
- package/dist/gateway/types.js +3 -0
- package/dist/gateway/types.js.map +1 -0
- package/dist/scripts/relay-genesis.d.ts +1 -0
- package/dist/scripts/relay-genesis.js +120 -0
- package/dist/scripts/relay-genesis.js.map +1 -0
- package/dist/scripts/relay-retarget.d.ts +1 -0
- package/dist/scripts/relay-retarget.js +115 -0
- package/dist/scripts/relay-retarget.js.map +1 -0
- package/dist/wallet/index.d.ts +1 -1
- package/dist/wallet/index.js +3 -1
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/utxo.d.ts +4 -4
- package/dist/wallet/utxo.js +20 -7
- package/dist/wallet/utxo.js.map +1 -1
- package/package.json +12 -10
- package/dist/gateway.d.ts +0 -30
- package/dist/gateway.js +0 -56
- package/dist/gateway.js.map +0 -1
- package/dist/wallet/address.d.ts +0 -2
- package/dist/wallet/address.js +0 -19
- package/dist/wallet/address.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { GatewayQuoteParams } from "./types";
|
|
2
|
+
import { Token as TokenInfo } from "./tokens";
|
|
3
|
+
export declare enum Chains {
|
|
4
|
+
Bitcoin = "bitcoin",
|
|
5
|
+
BOB = "bob",
|
|
6
|
+
BOBSepolia = "bobsepolia"
|
|
7
|
+
}
|
|
8
|
+
type EvmAddress = string;
|
|
9
|
+
type GatewayQuote = {
|
|
10
|
+
gatewayAddress: EvmAddress;
|
|
11
|
+
dustThreshold: number;
|
|
12
|
+
satoshis: number;
|
|
13
|
+
fee: number;
|
|
14
|
+
bitcoinAddress: string;
|
|
15
|
+
txProofDifficultyFactor: number;
|
|
16
|
+
strategyAddress?: EvmAddress;
|
|
17
|
+
};
|
|
18
|
+
type GatewayOrderResponse = {
|
|
19
|
+
gatewayAddress: EvmAddress;
|
|
20
|
+
tokenAddress: EvmAddress;
|
|
21
|
+
txid: string;
|
|
22
|
+
status: boolean;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
tokens: string;
|
|
25
|
+
satoshis: number;
|
|
26
|
+
fee: number;
|
|
27
|
+
txProofDifficultyFactor: number;
|
|
28
|
+
strategyAddress?: EvmAddress;
|
|
29
|
+
satsToConvertToEth: number;
|
|
30
|
+
};
|
|
31
|
+
type GatewayOrder = Omit<GatewayOrderResponse & {
|
|
32
|
+
gasRefill: number;
|
|
33
|
+
}, "satsToConvertToEth">;
|
|
34
|
+
type GatewayCreateOrderResponse = {
|
|
35
|
+
uuid: string;
|
|
36
|
+
opReturnHash: string;
|
|
37
|
+
};
|
|
38
|
+
type GatewayStartOrderResult = GatewayCreateOrderResponse & {
|
|
39
|
+
bitcoinAddress: string;
|
|
40
|
+
satoshis: number;
|
|
41
|
+
psbtBase64?: string;
|
|
42
|
+
};
|
|
43
|
+
export declare const MAINNET_GATEWAY_BASE_URL = "https://gateway-api-mainnet.gobob.xyz";
|
|
44
|
+
export declare const TESTNET_GATEWAY_BASE_URL = "https://gateway-api-testnet.gobob.xyz";
|
|
45
|
+
export declare class GatewayApiClient {
|
|
46
|
+
private network;
|
|
47
|
+
private baseUrl;
|
|
48
|
+
constructor(networkOrUrl?: string);
|
|
49
|
+
getQuote(params: GatewayQuoteParams): Promise<GatewayQuote>;
|
|
50
|
+
startOrder(gatewayQuote: GatewayQuote, params: GatewayQuoteParams): Promise<GatewayStartOrderResult>;
|
|
51
|
+
finalizeOrder(uuid: string, bitcoinTxHex: string): Promise<void>;
|
|
52
|
+
getOrders(userAddress: EvmAddress): Promise<GatewayOrder[]>;
|
|
53
|
+
getTokens(): Promise<EvmAddress[]>;
|
|
54
|
+
getTokensInfo(): Promise<TokenInfo[]>;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GatewayApiClient = exports.TESTNET_GATEWAY_BASE_URL = exports.MAINNET_GATEWAY_BASE_URL = exports.Chains = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const tokens_1 = require("./tokens");
|
|
6
|
+
const wallet_1 = require("../wallet");
|
|
7
|
+
var Chains;
|
|
8
|
+
(function (Chains) {
|
|
9
|
+
Chains["Bitcoin"] = "bitcoin";
|
|
10
|
+
Chains["BOB"] = "bob";
|
|
11
|
+
Chains["BOBSepolia"] = "bobsepolia";
|
|
12
|
+
})(Chains || (exports.Chains = Chains = {}));
|
|
13
|
+
;
|
|
14
|
+
exports.MAINNET_GATEWAY_BASE_URL = "https://gateway-api-mainnet.gobob.xyz";
|
|
15
|
+
exports.TESTNET_GATEWAY_BASE_URL = "https://gateway-api-testnet.gobob.xyz";
|
|
16
|
+
var Network;
|
|
17
|
+
(function (Network) {
|
|
18
|
+
Network[Network["Mainnet"] = 0] = "Mainnet";
|
|
19
|
+
Network[Network["Testnet"] = 1] = "Testnet";
|
|
20
|
+
})(Network || (Network = {}));
|
|
21
|
+
class GatewayApiClient {
|
|
22
|
+
constructor(networkOrUrl = "mainnet") {
|
|
23
|
+
switch (networkOrUrl) {
|
|
24
|
+
case "mainnet":
|
|
25
|
+
case "bob":
|
|
26
|
+
this.network = Network.Mainnet;
|
|
27
|
+
this.baseUrl = exports.MAINNET_GATEWAY_BASE_URL;
|
|
28
|
+
break;
|
|
29
|
+
case "testnet":
|
|
30
|
+
case "bobSepolia":
|
|
31
|
+
this.network = Network.Testnet;
|
|
32
|
+
this.baseUrl = exports.TESTNET_GATEWAY_BASE_URL;
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
this.baseUrl = networkOrUrl;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async getQuote(params) {
|
|
39
|
+
const isMainnet = params.toChain === 60808 || typeof params.toChain === "string" && params.toChain.toLowerCase() === Chains.BOB;
|
|
40
|
+
const isTestnet = params.toChain === 808813 || typeof params.toChain === "string" && params.toChain.toLowerCase() === Chains.BOBSepolia;
|
|
41
|
+
const toToken = params.toToken.toLowerCase();
|
|
42
|
+
let outputToken = "";
|
|
43
|
+
if (toToken.startsWith("0x")) {
|
|
44
|
+
outputToken = toToken;
|
|
45
|
+
}
|
|
46
|
+
else if (toToken in tokens_1.SYMBOL_LOOKUP) {
|
|
47
|
+
if (isMainnet && this.network === Network.Mainnet) {
|
|
48
|
+
outputToken = tokens_1.SYMBOL_LOOKUP[toToken].bob;
|
|
49
|
+
}
|
|
50
|
+
else if (isTestnet && this.network === Network.Testnet) {
|
|
51
|
+
outputToken = tokens_1.SYMBOL_LOOKUP[toToken].bobSepolia;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
throw new Error('Unknown network');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw new Error('Unknown output token');
|
|
59
|
+
}
|
|
60
|
+
const atomicAmount = params.amount;
|
|
61
|
+
const response = await fetch(`${this.baseUrl}/quote/${outputToken}/${atomicAmount || ''}`, {
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
Accept: 'application/json'
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return await response.json();
|
|
68
|
+
}
|
|
69
|
+
async startOrder(gatewayQuote, params) {
|
|
70
|
+
const request = {
|
|
71
|
+
gatewayAddress: gatewayQuote.gatewayAddress,
|
|
72
|
+
strategyAddress: gatewayQuote.strategyAddress,
|
|
73
|
+
satsToConvertToEth: params.gasRefill || 0,
|
|
74
|
+
userAddress: params.toUserAddress,
|
|
75
|
+
gatewayExtraData: undefined,
|
|
76
|
+
strategyExtraData: undefined,
|
|
77
|
+
satoshis: gatewayQuote.satoshis,
|
|
78
|
+
};
|
|
79
|
+
const response = await fetch(`${this.baseUrl}/order`, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
Accept: 'application/json'
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify(request)
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new Error('Failed to create order');
|
|
89
|
+
}
|
|
90
|
+
const data = await response.json();
|
|
91
|
+
if (data.opReturnHash != calculateOpReturnHash(request)) {
|
|
92
|
+
throw new Error('Invalid OP_RETURN hash');
|
|
93
|
+
}
|
|
94
|
+
let psbtBase64;
|
|
95
|
+
if (params.fromUserAddress && typeof params.fromChain === "string" && params.fromChain.toLowerCase() === Chains.Bitcoin) {
|
|
96
|
+
psbtBase64 = await (0, wallet_1.createBitcoinPsbt)(params.fromUserAddress, gatewayQuote.bitcoinAddress, gatewayQuote.satoshis, params.fromUserPublicKey, data.opReturnHash, gatewayQuote.txProofDifficultyFactor);
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
uuid: data.uuid,
|
|
100
|
+
opReturnHash: data.opReturnHash,
|
|
101
|
+
bitcoinAddress: gatewayQuote.bitcoinAddress,
|
|
102
|
+
satoshis: gatewayQuote.satoshis,
|
|
103
|
+
psbtBase64,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
async finalizeOrder(uuid, bitcoinTxHex) {
|
|
107
|
+
const response = await fetch(`${this.baseUrl}/order/${uuid}`, {
|
|
108
|
+
method: 'PATCH',
|
|
109
|
+
headers: {
|
|
110
|
+
'Content-Type': 'application/json',
|
|
111
|
+
Accept: 'application/json'
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({ bitcoinTx: bitcoinTxHex })
|
|
114
|
+
});
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
throw new Error('Failed to update order');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async getOrders(userAddress) {
|
|
120
|
+
const response = await fetch(`${this.baseUrl}/orders/${userAddress}`, {
|
|
121
|
+
method: 'GET',
|
|
122
|
+
headers: {
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
Accept: 'application/json'
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const orders = await response.json();
|
|
128
|
+
return orders.map(order => { return { gasRefill: order.satsToConvertToEth, ...order }; });
|
|
129
|
+
}
|
|
130
|
+
async getTokens() {
|
|
131
|
+
const response = await fetch(`${this.baseUrl}/tokens`, {
|
|
132
|
+
method: 'GET',
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
Accept: 'application/json'
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return response.json();
|
|
139
|
+
}
|
|
140
|
+
async getTokensInfo() {
|
|
141
|
+
const tokens = await this.getTokens();
|
|
142
|
+
return tokens
|
|
143
|
+
.map(token => tokens_1.ADDRESS_LOOKUP[token])
|
|
144
|
+
.filter(token => token !== undefined);
|
|
145
|
+
;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.GatewayApiClient = GatewayApiClient;
|
|
149
|
+
function calculateOpReturnHash(req) {
|
|
150
|
+
const abiCoder = new ethers_1.AbiCoder();
|
|
151
|
+
return ethers_1.ethers.keccak256(abiCoder.encode(["address", "address", "uint256", "address", "bytes", "bytes"], [
|
|
152
|
+
req.gatewayAddress,
|
|
153
|
+
req.strategyAddress || ethers_1.ethers.ZeroAddress,
|
|
154
|
+
req.satsToConvertToEth,
|
|
155
|
+
req.userAddress,
|
|
156
|
+
req.gatewayExtraData || "0x",
|
|
157
|
+
req.strategyExtraData || "0x"
|
|
158
|
+
]));
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/gateway/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GatewaySDK = void 0;
|
|
4
|
+
var client_1 = require("./client");
|
|
5
|
+
Object.defineProperty(exports, "GatewaySDK", { enumerable: true, get: function () { return client_1.GatewayApiClient; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gateway/index.ts"],"names":[],"mappings":";;;AAAA,mCAA0D;AAAjD,oGAAA,gBAAgB,OAAc"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ADDRESS_LOOKUP = exports.SYMBOL_LOOKUP = void 0;
|
|
4
|
+
const TOKENS = {
|
|
5
|
+
"tBTC": {
|
|
6
|
+
name: "tBTC v2",
|
|
7
|
+
symbol: "tBTC",
|
|
8
|
+
bob: "0xBBa2eF945D523C4e2608C9E1214C2Cc64D4fc2e2",
|
|
9
|
+
bobSepolia: "0x6744bAbDf02DCF578EA173A9F0637771A9e1c4d0",
|
|
10
|
+
},
|
|
11
|
+
"WBTC": {
|
|
12
|
+
name: "Wrapped BTC",
|
|
13
|
+
symbol: "WBTC",
|
|
14
|
+
bob: "0x03C7054BCB39f7b2e5B2c7AcB37583e32D70Cfa3",
|
|
15
|
+
bobSepolia: "0xe51e40e15e6e1496a0981f90Ca1D632545bdB519",
|
|
16
|
+
},
|
|
17
|
+
"sbtBTC": {
|
|
18
|
+
name: "sb tBTC v2",
|
|
19
|
+
symbol: "sbtBTC",
|
|
20
|
+
bob: "0x2925dF9Eb2092B53B06A06353A7249aF3a8B139e",
|
|
21
|
+
bobSepolia: "",
|
|
22
|
+
},
|
|
23
|
+
"sbWBTC": {
|
|
24
|
+
name: "sb Wrapped BTC",
|
|
25
|
+
symbol: "sbWBTC",
|
|
26
|
+
bob: "0x5c46D274ed8AbCAe2964B63c0360ad3Ccc384dAa",
|
|
27
|
+
bobSepolia: "",
|
|
28
|
+
},
|
|
29
|
+
"seTBTC": {
|
|
30
|
+
name: "Segment TBTC",
|
|
31
|
+
symbol: "seTBTC",
|
|
32
|
+
bob: "0xD30288EA9873f376016A0250433b7eA375676077",
|
|
33
|
+
bobSepolia: "",
|
|
34
|
+
},
|
|
35
|
+
"seWBTC": {
|
|
36
|
+
name: "Segment WBTC",
|
|
37
|
+
symbol: "seWBTC",
|
|
38
|
+
bob: "0x6265C05158f672016B771D6Fb7422823ed2CbcDd",
|
|
39
|
+
bobSepolia: "",
|
|
40
|
+
},
|
|
41
|
+
"stmtBTC": {
|
|
42
|
+
name: "Staked mtBTC",
|
|
43
|
+
symbol: "stmtBTC",
|
|
44
|
+
bob: "",
|
|
45
|
+
bobSepolia: "0xc4229678b65e2D9384FDf96F2E5D512d6eeC0C77",
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
exports.SYMBOL_LOOKUP = {};
|
|
49
|
+
exports.ADDRESS_LOOKUP = {};
|
|
50
|
+
for (const key in TOKENS) {
|
|
51
|
+
const token = TOKENS[key];
|
|
52
|
+
const lowerBob = token.bob.toLowerCase();
|
|
53
|
+
const lowerBobSepolia = token.bobSepolia.toLowerCase();
|
|
54
|
+
const lowercasedToken = {
|
|
55
|
+
name: token.name,
|
|
56
|
+
symbol: token.symbol,
|
|
57
|
+
bob: lowerBob,
|
|
58
|
+
bobSepolia: lowerBobSepolia,
|
|
59
|
+
};
|
|
60
|
+
exports.SYMBOL_LOOKUP[key.toLowerCase()] = lowercasedToken;
|
|
61
|
+
exports.ADDRESS_LOOKUP[lowerBob] = lowercasedToken;
|
|
62
|
+
exports.ADDRESS_LOOKUP[lowerBobSepolia] = lowercasedToken;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/gateway/tokens.ts"],"names":[],"mappings":";;;AAOA,MAAM,MAAM,GAA6B;IACrC,MAAM,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,MAAM;QACd,GAAG,EAAE,4CAA4C;QACjD,UAAU,EAAE,4CAA4C;KAC3D;IACD,MAAM,EAAE;QACJ,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,MAAM;QACd,GAAG,EAAE,4CAA4C;QACjD,UAAU,EAAE,4CAA4C;KAC3D;IACD,QAAQ,EAAE;QACN,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,QAAQ;QAChB,GAAG,EAAE,4CAA4C;QACjD,UAAU,EAAE,EAAE;KACjB;IACD,QAAQ,EAAE;QACN,IAAI,EAAE,gBAAgB;QACtB,MAAM,EAAE,QAAQ;QAChB,GAAG,EAAE,4CAA4C;QACjD,UAAU,EAAE,EAAE;KACjB;IACD,QAAQ,EAAE;QACN,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,QAAQ;QAChB,GAAG,EAAE,4CAA4C;QACjD,UAAU,EAAE,EAAE;KACjB;IACD,QAAQ,EAAE;QACN,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,QAAQ;QAChB,GAAG,EAAE,4CAA4C;QACjD,UAAU,EAAE,EAAE;KACjB;IACD,SAAS,EAAE;QACP,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,SAAS;QACjB,GAAG,EAAE,EAAE;QACP,UAAU,EAAE,4CAA4C;KAC3D;CACJ,CAAC;AAGW,QAAA,aAAa,GAA6B,EAAE,CAAC;AAC7C,QAAA,cAAc,GAAiC,EAAE,CAAC;AAE/D,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAE1B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;IACzC,MAAM,eAAe,GAAG,KAAK,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;IAEvD,MAAM,eAAe,GAAU;QAC3B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,EAAE,QAAQ;QACb,UAAU,EAAE,eAAe;KAC9B,CAAC;IAEF,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,eAAe,CAAC;IACnD,sBAAc,CAAC,QAAQ,CAAC,GAAG,eAAe,CAAC;IAC3C,sBAAc,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type ChainSlug = string | number;
|
|
2
|
+
type TokenSymbol = string;
|
|
3
|
+
export interface GatewayQuoteParams {
|
|
4
|
+
fromChain?: ChainSlug;
|
|
5
|
+
toChain: ChainSlug;
|
|
6
|
+
fromToken?: TokenSymbol;
|
|
7
|
+
toToken: TokenSymbol;
|
|
8
|
+
fromUserAddress?: string;
|
|
9
|
+
fromUserPublicKey?: string;
|
|
10
|
+
toUserAddress: string;
|
|
11
|
+
amount: number | string;
|
|
12
|
+
maxSlippage?: number;
|
|
13
|
+
affiliateId?: string;
|
|
14
|
+
type?: 'swap' | 'deposit' | 'withdraw' | 'claim';
|
|
15
|
+
fee?: number;
|
|
16
|
+
gasRefill?: number;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/gateway/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const esplora_1 = require("../esplora");
|
|
7
|
+
const yargs_1 = __importDefault(require("yargs"));
|
|
8
|
+
const helpers_1 = require("yargs/helpers");
|
|
9
|
+
const node_child_process_1 = require("node:child_process");
|
|
10
|
+
const args = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
11
|
+
.option("init-height", {
|
|
12
|
+
description: "Height of the bitcoin chain to initialize the relay at",
|
|
13
|
+
demandOption: true,
|
|
14
|
+
})
|
|
15
|
+
.option("private-key", {
|
|
16
|
+
description: "Private key to submit with",
|
|
17
|
+
type: "string",
|
|
18
|
+
})
|
|
19
|
+
.option("dev", {
|
|
20
|
+
description: "Deploy the contracts locally",
|
|
21
|
+
type: "boolean",
|
|
22
|
+
})
|
|
23
|
+
.option("testnet", {
|
|
24
|
+
description: "Use the testnet relay contract which can override the difficulty",
|
|
25
|
+
type: "boolean",
|
|
26
|
+
})
|
|
27
|
+
.option("proof-length", {
|
|
28
|
+
description: "The default proof length for retargets",
|
|
29
|
+
type: "number",
|
|
30
|
+
default: 1,
|
|
31
|
+
})
|
|
32
|
+
.option("network", {
|
|
33
|
+
description: "Bitcoin network to use",
|
|
34
|
+
type: "string",
|
|
35
|
+
demandOption: true,
|
|
36
|
+
})
|
|
37
|
+
.option("rpc-url", {
|
|
38
|
+
description: "ETH RPC URL",
|
|
39
|
+
type: "string",
|
|
40
|
+
})
|
|
41
|
+
.option("verifier-url", {
|
|
42
|
+
description: "Verifier URL",
|
|
43
|
+
type: "string",
|
|
44
|
+
})
|
|
45
|
+
.argv;
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
console.log("Error thrown by script:");
|
|
48
|
+
console.log(err);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|
|
51
|
+
function range(size, startAt = 0) {
|
|
52
|
+
return [...Array(size).keys()].map(i => i + startAt);
|
|
53
|
+
}
|
|
54
|
+
async function getRetargetHeaders(esploraClient, nextRetargetHeight, proofLength) {
|
|
55
|
+
const beforeRetarget = await Promise.all(range(proofLength, nextRetargetHeight - proofLength).map(height => esploraClient.getBlockHeaderAt(height)));
|
|
56
|
+
const afterRetarget = await Promise.all(range(proofLength, nextRetargetHeight).map(height => esploraClient.getBlockHeaderAt(height)));
|
|
57
|
+
return beforeRetarget.concat(afterRetarget).join("");
|
|
58
|
+
}
|
|
59
|
+
async function main() {
|
|
60
|
+
const esploraClient = new esplora_1.DefaultEsploraClient(args["network"]);
|
|
61
|
+
let initHeight = args["init-height"];
|
|
62
|
+
if (initHeight == "latest") {
|
|
63
|
+
const currentHeight = await esploraClient.getLatestHeight();
|
|
64
|
+
initHeight = currentHeight - (currentHeight % 2016) - 2016;
|
|
65
|
+
console.log(`Using block ${initHeight}`);
|
|
66
|
+
}
|
|
67
|
+
if ((initHeight % 2016) != 0) {
|
|
68
|
+
throw new Error("Invalid genesis height: must be multiple of 2016");
|
|
69
|
+
}
|
|
70
|
+
const genesis = await esploraClient.getBlockHeaderAt(initHeight);
|
|
71
|
+
const proofLength = args["proof-length"];
|
|
72
|
+
const nextRetargetHeight = initHeight + 2016;
|
|
73
|
+
console.log(`Next retarget height: ${nextRetargetHeight}`);
|
|
74
|
+
const retargetHeaders = await getRetargetHeaders(esploraClient, nextRetargetHeight, proofLength);
|
|
75
|
+
let rpcUrl;
|
|
76
|
+
let verifyOpts;
|
|
77
|
+
if (args["dev"]) {
|
|
78
|
+
rpcUrl = "http://localhost:8545";
|
|
79
|
+
}
|
|
80
|
+
else if (args["rpc-url"] == "testnet") {
|
|
81
|
+
rpcUrl = "https://bob-sepolia.rpc.gobob.xyz/";
|
|
82
|
+
verifyOpts = "--verify --verifier blockscout --verifier-url 'https://bob-sepolia.explorer.gobob.xyz/api'";
|
|
83
|
+
}
|
|
84
|
+
else if (args["rpc-url"] == "mainnet") {
|
|
85
|
+
rpcUrl = "https://rpc.gobob.xyz/";
|
|
86
|
+
verifyOpts = "--verify --verifier blockscout --verifier-url 'https://explorer.gobob.xyz/api'";
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
rpcUrl = args["rpc-url"];
|
|
90
|
+
verifyOpts = `--verify --verifier blockscout --verifier-url ${args["verifier-url"]}`;
|
|
91
|
+
}
|
|
92
|
+
let privateKey;
|
|
93
|
+
if (args["private-key"]) {
|
|
94
|
+
privateKey = args["private-key"];
|
|
95
|
+
}
|
|
96
|
+
else if (args["dev"]) {
|
|
97
|
+
privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
throw new Error("No private key");
|
|
101
|
+
}
|
|
102
|
+
let env = [
|
|
103
|
+
`GENESIS_PROOF_LENGTH=${proofLength}`,
|
|
104
|
+
`GENESIS_HEIGHT=${initHeight}`,
|
|
105
|
+
`GENESIS_HEADER=${genesis}`,
|
|
106
|
+
`RETARGET_HEADERS=${retargetHeaders}`,
|
|
107
|
+
`PRIVATE_KEY=${privateKey}`,
|
|
108
|
+
];
|
|
109
|
+
if (args["testnet"]) {
|
|
110
|
+
env.push("TESTNET=true");
|
|
111
|
+
}
|
|
112
|
+
(0, node_child_process_1.exec)(`${env.join(" ")} forge script ../script/RelayGenesis.s.sol:RelayGenesisScript --rpc-url '${rpcUrl}' ${verifyOpts} --broadcast --priority-gas-price 1`, { maxBuffer: 1024 * 5000 }, (err, stdout, stderr) => {
|
|
113
|
+
if (err) {
|
|
114
|
+
throw new Error(`Failed to run command: ${err}`);
|
|
115
|
+
}
|
|
116
|
+
console.log(`stdout: ${stdout}`);
|
|
117
|
+
console.log(`stderr: ${stderr}`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=relay-genesis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-genesis.js","sourceRoot":"","sources":["../../src/scripts/relay-genesis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const esplora_1 = require("../esplora");
|
|
7
|
+
const yargs_1 = __importDefault(require("yargs"));
|
|
8
|
+
const helpers_1 = require("yargs/helpers");
|
|
9
|
+
const node_child_process_1 = require("node:child_process");
|
|
10
|
+
const args = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
11
|
+
.env('RELAY')
|
|
12
|
+
.option("private-key", {
|
|
13
|
+
description: "Private key to submit with",
|
|
14
|
+
type: "string",
|
|
15
|
+
})
|
|
16
|
+
.option("dev", {
|
|
17
|
+
description: "Deploy the contracts locally",
|
|
18
|
+
type: "boolean",
|
|
19
|
+
})
|
|
20
|
+
.option("network", {
|
|
21
|
+
description: "Bitcoin network to use",
|
|
22
|
+
type: "string",
|
|
23
|
+
demandOption: true,
|
|
24
|
+
})
|
|
25
|
+
.option("rpc-url", {
|
|
26
|
+
description: "ETH RPC URL",
|
|
27
|
+
type: "string",
|
|
28
|
+
})
|
|
29
|
+
.option("relay-address", {
|
|
30
|
+
description: "Relay address",
|
|
31
|
+
type: "string",
|
|
32
|
+
demandOption: true,
|
|
33
|
+
})
|
|
34
|
+
.argv;
|
|
35
|
+
main().catch((err) => {
|
|
36
|
+
console.log("Error thrown by script:");
|
|
37
|
+
console.log(err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
40
|
+
function range(size, startAt = 0) {
|
|
41
|
+
return [...Array(size).keys()].map(i => i + startAt);
|
|
42
|
+
}
|
|
43
|
+
async function getRetargetHeaders(esploraClient, nextRetargetHeight, proofLength) {
|
|
44
|
+
const beforeRetarget = await Promise.all(range(proofLength, nextRetargetHeight - proofLength).map(height => esploraClient.getBlockHeaderAt(height)));
|
|
45
|
+
const afterRetarget = await Promise.all(range(proofLength, nextRetargetHeight).map(height => esploraClient.getBlockHeaderAt(height)));
|
|
46
|
+
return beforeRetarget.concat(afterRetarget).join("");
|
|
47
|
+
}
|
|
48
|
+
async function main() {
|
|
49
|
+
const esploraClient = new esplora_1.DefaultEsploraClient(args["network"]);
|
|
50
|
+
let privateKey;
|
|
51
|
+
if (args["private-key"]) {
|
|
52
|
+
privateKey = args["private-key"];
|
|
53
|
+
}
|
|
54
|
+
else if (args["dev"]) {
|
|
55
|
+
privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw new Error("No private key");
|
|
59
|
+
}
|
|
60
|
+
const relayAddress = args["relay-address"];
|
|
61
|
+
let rpcUrl;
|
|
62
|
+
if (args["dev"]) {
|
|
63
|
+
rpcUrl = "http://localhost:8545";
|
|
64
|
+
}
|
|
65
|
+
else if (args["rpc-url"] == "testnet") {
|
|
66
|
+
rpcUrl = "https://bob-sepolia.rpc.gobob.xyz/";
|
|
67
|
+
}
|
|
68
|
+
else if (args["rpc-url"] == "mainnet") {
|
|
69
|
+
rpcUrl = "https://rpc.gobob.xyz/";
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
rpcUrl = args["rpc-url"];
|
|
73
|
+
}
|
|
74
|
+
const currentEpoch = await new Promise((resolve, reject) => {
|
|
75
|
+
(0, node_child_process_1.exec)(`cast call ${relayAddress} "currentEpoch() (uint256)" --rpc-url '${rpcUrl}'`, (err, stdout, _stderr) => {
|
|
76
|
+
if (err)
|
|
77
|
+
reject(`Failed to run command: ${err}`);
|
|
78
|
+
resolve(Number.parseInt(stdout));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
console.log(`Current epoch: ${currentEpoch}`);
|
|
82
|
+
const proofLength = await new Promise((resolve, reject) => {
|
|
83
|
+
(0, node_child_process_1.exec)(`cast call ${relayAddress} "proofLength() (uint256)" --rpc-url '${rpcUrl}'`, (err, stdout, _stderr) => {
|
|
84
|
+
if (err)
|
|
85
|
+
reject(`Failed to run command: ${err}`);
|
|
86
|
+
resolve(Number.parseInt(stdout));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
console.log(`Proof length: ${proofLength}`);
|
|
90
|
+
const nextEpoch = currentEpoch + 1;
|
|
91
|
+
const nextRetargetHeight = nextEpoch * 2016;
|
|
92
|
+
console.log(`Next epoch: ${nextEpoch}`);
|
|
93
|
+
console.log(`Next retarget height: ${nextRetargetHeight}`);
|
|
94
|
+
try {
|
|
95
|
+
await esploraClient.getBlockHash(nextRetargetHeight + proofLength);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
console.log(`Cannot retarget without ${proofLength} headers after ${nextRetargetHeight}. Exiting.`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const retargetHeaders = await getRetargetHeaders(esploraClient, nextRetargetHeight, proofLength);
|
|
102
|
+
let env = {
|
|
103
|
+
'RELAY_ADDRESS': relayAddress,
|
|
104
|
+
'RETARGET_HEADERS': retargetHeaders,
|
|
105
|
+
'PRIVATE_KEY': privateKey,
|
|
106
|
+
};
|
|
107
|
+
(0, node_child_process_1.exec)(`forge script ../script/RelayRetarget.s.sol:RelayRetargetScript --rpc-url '${rpcUrl}' --broadcast --priority-gas-price 1`, { env: { ...process.env, ...env } }, (err, stdout, stderr) => {
|
|
108
|
+
if (err) {
|
|
109
|
+
throw new Error(`Failed to run command: ${err}`);
|
|
110
|
+
}
|
|
111
|
+
console.log(`stdout: ${stdout}`);
|
|
112
|
+
console.log(`stderr: ${stderr}`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=relay-retarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-retarget.js","sourceRoot":"","sources":["../../src/scripts/relay-retarget.ts"],"names":[],"mappings":"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"}
|
package/dist/wallet/index.d.ts
CHANGED
package/dist/wallet/index.js
CHANGED
|
@@ -14,6 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
|
|
17
|
+
exports.isValidBtcAddress = void 0;
|
|
18
18
|
__exportStar(require("./utxo"), exports);
|
|
19
|
+
var bitcoin_address_validation_1 = require("bitcoin-address-validation");
|
|
20
|
+
Object.defineProperty(exports, "isValidBtcAddress", { enumerable: true, get: function () { return bitcoin_address_validation_1.validate; } });
|
|
19
21
|
//# sourceMappingURL=index.js.map
|
package/dist/wallet/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wallet/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wallet/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,yCAAuB;AAEvB,yEAA2E;AAAlE,+HAAA,QAAQ,OAAqB"}
|
package/dist/wallet/utxo.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Transaction } from '@scure/btc-signer';
|
|
2
|
-
import { AddressType } from 'bitcoin-address-validation';
|
|
2
|
+
import { AddressType, Network } from 'bitcoin-address-validation';
|
|
3
3
|
import { UTXO } from '../esplora';
|
|
4
|
-
export type BitcoinNetworkName =
|
|
4
|
+
export type BitcoinNetworkName = Exclude<Network, "regtest">;
|
|
5
5
|
export declare const getBtcNetwork: (name: BitcoinNetworkName) => {
|
|
6
6
|
bech32: string;
|
|
7
7
|
pubKeyHash: number;
|
|
@@ -19,5 +19,5 @@ export interface Input {
|
|
|
19
19
|
};
|
|
20
20
|
nonWitnessUtxo?: Uint8Array;
|
|
21
21
|
}
|
|
22
|
-
export declare function
|
|
23
|
-
export declare function getInputFromUtxoAndTx(network: BitcoinNetworkName, utxo: UTXO, transaction: Transaction, addressType: AddressType,
|
|
22
|
+
export declare function createBitcoinPsbt(fromAddress: string, toAddress: string, amount: number, publicKey?: string, opReturnData?: string, confirmationTarget?: number): Promise<string>;
|
|
23
|
+
export declare function getInputFromUtxoAndTx(network: BitcoinNetworkName, utxo: UTXO, transaction: Transaction, addressType: AddressType, publicKey?: string): Input;
|
package/dist/wallet/utxo.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getBtcNetwork = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.createBitcoinPsbt = createBitcoinPsbt;
|
|
5
5
|
exports.getInputFromUtxoAndTx = getInputFromUtxoAndTx;
|
|
6
6
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
7
7
|
const base_1 = require("@scure/base");
|
|
@@ -15,8 +15,18 @@ const getBtcNetwork = (name) => {
|
|
|
15
15
|
return bitcoinNetworks[name];
|
|
16
16
|
};
|
|
17
17
|
exports.getBtcNetwork = getBtcNetwork;
|
|
18
|
-
async function
|
|
19
|
-
const
|
|
18
|
+
async function createBitcoinPsbt(fromAddress, toAddress, amount, publicKey, opReturnData, confirmationTarget = 3) {
|
|
19
|
+
const addressInfo = (0, bitcoin_address_validation_1.getAddressInfo)(fromAddress);
|
|
20
|
+
const network = addressInfo.network;
|
|
21
|
+
if (network === "regtest") {
|
|
22
|
+
throw new Error("Bitcoin regtest not supported");
|
|
23
|
+
}
|
|
24
|
+
if (addressInfo.type === (bitcoin_address_validation_1.AddressType.p2sh || bitcoin_address_validation_1.AddressType.p2wsh)) {
|
|
25
|
+
if (!publicKey) {
|
|
26
|
+
throw new Error('Public key is required to spend from the selected address type');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const esploraClient = new esplora_1.DefaultEsploraClient(addressInfo.network);
|
|
20
30
|
const [confirmedUtxos, feeRate] = await Promise.all([
|
|
21
31
|
esploraClient.getAddressUtxos(fromAddress),
|
|
22
32
|
esploraClient.getFeeEstimate(confirmationTarget)
|
|
@@ -28,7 +38,7 @@ async function createTransfer(network, addressType, fromAddress, toAddress, amou
|
|
|
28
38
|
await Promise.all(confirmedUtxos.map(async (utxo) => {
|
|
29
39
|
const hex = await esploraClient.getTransactionHex(utxo.txid);
|
|
30
40
|
const transaction = btc_signer_1.Transaction.fromRaw(Buffer.from(hex, 'hex'), { allowUnknownOutputs: true });
|
|
31
|
-
const input = getInputFromUtxoAndTx(network, utxo, transaction,
|
|
41
|
+
const input = getInputFromUtxoAndTx(network, utxo, transaction, addressInfo.type, publicKey);
|
|
32
42
|
possibleInputs.push(input);
|
|
33
43
|
}));
|
|
34
44
|
const outputs = [
|
|
@@ -58,13 +68,16 @@ async function createTransfer(network, addressType, fromAddress, toAddress, amou
|
|
|
58
68
|
if (!transaction || !transaction.tx) {
|
|
59
69
|
throw new Error('Failed to create transaction. Do you have enough funds?');
|
|
60
70
|
}
|
|
61
|
-
return transaction.tx;
|
|
71
|
+
return base_1.base64.encode(transaction.tx.toPSBT(0));
|
|
62
72
|
}
|
|
63
|
-
function getInputFromUtxoAndTx(network, utxo, transaction, addressType,
|
|
73
|
+
function getInputFromUtxoAndTx(network, utxo, transaction, addressType, publicKey) {
|
|
64
74
|
const output = transaction.getOutput(utxo.vout);
|
|
65
75
|
let redeemScript = {};
|
|
66
76
|
if (addressType === bitcoin_address_validation_1.AddressType.p2sh) {
|
|
67
|
-
|
|
77
|
+
if (!publicKey) {
|
|
78
|
+
throw new Error("Bitcoin P2SH not supported without public key");
|
|
79
|
+
}
|
|
80
|
+
const inner = (0, btc_signer_1.p2wpkh)(Buffer.from(publicKey, 'hex'), (0, exports.getBtcNetwork)(network));
|
|
68
81
|
redeemScript = (0, btc_signer_1.p2sh)(inner);
|
|
69
82
|
}
|
|
70
83
|
const scriptMixin = {
|
package/dist/wallet/utxo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utxo.js","sourceRoot":"","sources":["../../src/wallet/utxo.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"utxo.js","sourceRoot":"","sources":["../../src/wallet/utxo.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobob/bob-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "vitest run test/*.ts",
|
|
8
|
-
"deploy-relay": "ts-node scripts/relay-genesis.ts",
|
|
9
|
-
"update-relay": "ts-node scripts/relay-retarget.ts",
|
|
8
|
+
"deploy-relay": "ts-node src/scripts/relay-genesis.ts",
|
|
9
|
+
"update-relay": "ts-node src/scripts/relay-retarget.ts",
|
|
10
10
|
"build": "tsc -p tsconfig.json"
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
@@ -18,20 +18,22 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/node": "^
|
|
22
|
-
"@types/yargs": "^17.0.
|
|
21
|
+
"@types/node": "^22.5.0",
|
|
22
|
+
"@types/yargs": "^17.0.33",
|
|
23
23
|
"ecpair": "^2.1.0",
|
|
24
|
-
"mocha": "^10.3
|
|
24
|
+
"mocha": "^10.7.3",
|
|
25
|
+
"nock": "^14.0.0-beta.11",
|
|
25
26
|
"tiny-secp256k1": "^2.2.3",
|
|
26
27
|
"ts-node": "^10.0.0",
|
|
27
|
-
"typescript": "^5.4
|
|
28
|
-
"vitest": "^
|
|
28
|
+
"typescript": "^5.5.4",
|
|
29
|
+
"vitest": "^2.0.5",
|
|
29
30
|
"yargs": "^17.5.1"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"@scure/base": "^1.1.7",
|
|
33
34
|
"@scure/btc-signer": "^1.3.2",
|
|
34
35
|
"bitcoin-address-validation": "^2.2.3",
|
|
35
|
-
"bitcoinjs-lib": "^6.1.6"
|
|
36
|
+
"bitcoinjs-lib": "^6.1.6",
|
|
37
|
+
"ethers": "^6.13.2"
|
|
36
38
|
}
|
|
37
|
-
}
|
|
39
|
+
}
|
package/dist/gateway.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export type EvmAddress = string;
|
|
2
|
-
type GatewayQuote = {
|
|
3
|
-
onramp_address: EvmAddress;
|
|
4
|
-
dust_threshold: number;
|
|
5
|
-
satoshis: number;
|
|
6
|
-
fee: number;
|
|
7
|
-
gratuity: string;
|
|
8
|
-
bitcoin_address: string;
|
|
9
|
-
tx_proof_difficulty_factor: number;
|
|
10
|
-
};
|
|
11
|
-
type GatewayOrderResponse = {
|
|
12
|
-
onramp_address: EvmAddress;
|
|
13
|
-
token_address: EvmAddress;
|
|
14
|
-
txid: string;
|
|
15
|
-
status: boolean;
|
|
16
|
-
timestamp: number;
|
|
17
|
-
tokens: string;
|
|
18
|
-
satoshis: number;
|
|
19
|
-
fee: number;
|
|
20
|
-
tx_proof_difficulty_factor: number;
|
|
21
|
-
};
|
|
22
|
-
export declare class GatewayApiClient {
|
|
23
|
-
private baseUrl;
|
|
24
|
-
constructor(baseUrl: string);
|
|
25
|
-
getQuote(address: string, atomicAmount?: number | string): Promise<GatewayQuote>;
|
|
26
|
-
createOrder(contractAddress: string, userAddress: EvmAddress, atomicAmount: number | string): Promise<string>;
|
|
27
|
-
updateOrder(id: string, tx: string): Promise<void>;
|
|
28
|
-
getOrders(userAddress: EvmAddress): Promise<GatewayOrderResponse[]>;
|
|
29
|
-
}
|
|
30
|
-
export {};
|
package/dist/gateway.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GatewayApiClient = void 0;
|
|
4
|
-
class GatewayApiClient {
|
|
5
|
-
constructor(baseUrl) {
|
|
6
|
-
this.baseUrl = baseUrl;
|
|
7
|
-
}
|
|
8
|
-
async getQuote(address, atomicAmount) {
|
|
9
|
-
const response = await fetch(`${this.baseUrl}/quote/${address}/${atomicAmount || ''}`, {
|
|
10
|
-
headers: {
|
|
11
|
-
'Content-Type': 'application/json',
|
|
12
|
-
Accept: 'application/json'
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
return await response.json();
|
|
16
|
-
}
|
|
17
|
-
async createOrder(contractAddress, userAddress, atomicAmount) {
|
|
18
|
-
const response = await fetch(`${this.baseUrl}/order`, {
|
|
19
|
-
method: 'POST',
|
|
20
|
-
headers: {
|
|
21
|
-
'Content-Type': 'application/json',
|
|
22
|
-
Accept: 'application/json'
|
|
23
|
-
},
|
|
24
|
-
body: JSON.stringify({ onramp_address: contractAddress, user_address: userAddress, satoshis: atomicAmount })
|
|
25
|
-
});
|
|
26
|
-
if (!response.ok) {
|
|
27
|
-
throw new Error('Failed to create order');
|
|
28
|
-
}
|
|
29
|
-
return await response.json();
|
|
30
|
-
}
|
|
31
|
-
async updateOrder(id, tx) {
|
|
32
|
-
const response = await fetch(`${this.baseUrl}/order/${id}`, {
|
|
33
|
-
method: 'PATCH',
|
|
34
|
-
headers: {
|
|
35
|
-
'Content-Type': 'application/json',
|
|
36
|
-
Accept: 'application/json'
|
|
37
|
-
},
|
|
38
|
-
body: JSON.stringify({ bitcoin_tx: tx })
|
|
39
|
-
});
|
|
40
|
-
if (!response.ok) {
|
|
41
|
-
throw new Error('Failed to update order');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async getOrders(userAddress) {
|
|
45
|
-
const response = await fetch(`${this.baseUrl}/orders/${userAddress}`, {
|
|
46
|
-
method: 'GET',
|
|
47
|
-
headers: {
|
|
48
|
-
'Content-Type': 'application/json',
|
|
49
|
-
Accept: 'application/json'
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
return response.json();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
exports.GatewayApiClient = GatewayApiClient;
|
|
56
|
-
//# sourceMappingURL=gateway.js.map
|
package/dist/gateway.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":";;;AAwBA,MAAa,gBAAgB;IAGzB,YAAY,OAAe;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAe,EAAE,YAA8B;QAC1D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,UAAU,OAAO,IAAI,YAAY,IAAI,EAAE,EAAE,EAAE;YACnF,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;aAC7B;SACJ,CAAC,CAAC;QAEH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;IAGD,KAAK,CAAC,WAAW,CAAC,eAAuB,EAAE,WAAuB,EAAE,YAA6B;QAC7F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,QAAQ,EAAE;YAClD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;aAC7B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;SAC/G,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,EAAU;QACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,UAAU,EAAE,EAAE,EAAE;YACxD,MAAM,EAAE,OAAO;YACf,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;aAC7B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;SAC3C,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC9C,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,WAAuB;QACnC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,WAAW,WAAW,EAAE,EAAE;YAClE,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;aAC7B;SACJ,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACJ;AA9DD,4CA8DC"}
|
package/dist/wallet/address.d.ts
DELETED
package/dist/wallet/address.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isValidBtcAddress = void 0;
|
|
4
|
-
const BTC_MAINNET_REGEX = /\b([13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[ac-hj-np-zAC-HJ-NP-Z02-9]{11,71})\b/;
|
|
5
|
-
const BTC_TESTNET_REGEX = /\b([2mn][a-km-zA-HJ-NP-Z1-9]{25,34}|tb1[ac-hj-np-zAC-HJ-NP-Z02-9]{11,71})\b/;
|
|
6
|
-
const mainnetRegex = new RegExp(BTC_MAINNET_REGEX);
|
|
7
|
-
const testnetRegex = new RegExp(BTC_TESTNET_REGEX);
|
|
8
|
-
const isValidBtcAddress = (network, address) => {
|
|
9
|
-
switch (network) {
|
|
10
|
-
case 'mainnet':
|
|
11
|
-
return mainnetRegex.test(address);
|
|
12
|
-
case 'testnet':
|
|
13
|
-
return testnetRegex.test(address);
|
|
14
|
-
default:
|
|
15
|
-
throw new Error(`Invalid bitcoin network configured: ${network}. Valid values are: mainnet | testnet.`);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
exports.isValidBtcAddress = isValidBtcAddress;
|
|
19
|
-
//# sourceMappingURL=address.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"address.js","sourceRoot":"","sources":["../../src/wallet/address.ts"],"names":[],"mappings":";;;AAEA,MAAM,iBAAiB,GAAG,4EAA4E,CAAC;AACvG,MAAM,iBAAiB,GAAG,6EAA6E,CAAC;AAExG,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,iBAAiB,CAAC,CAAC;AACnD,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAE5C,MAAM,iBAAiB,GAAG,CAAC,OAA2B,EAAE,OAAe,EAAW,EAAE;IACzF,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,SAAS;YACZ,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,KAAK,SAAS;YACZ,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC;YACE,MAAM,IAAI,KAAK,CAAC,uCAAuC,OAAO,wCAAwC,CAAC,CAAC;IAC5G,CAAC;AACH,CAAC,CAAC;AATW,QAAA,iBAAiB,qBAS5B"}
|