emblem-vault-sdk 2.9.0 → 2.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +11 -0
- package/dist/bundle.js +45521 -24839
- package/dist/constants.js +35 -0
- package/dist/curated/metadata.json +1 -1
- package/dist/derive.js +17 -7
- package/dist/evm-operations.js +298 -0
- package/dist/index.js +123 -60
- package/dist/signing-messages.js +19 -0
- package/dist/utils.js +59 -19
- package/dist/vault-utils.js +71 -0
- package/docs/bundle.js +45521 -24839
- package/docs/index.html +8 -2
- package/package.json +4 -2
- package/src/constants.ts +47 -0
- package/src/curated/metadata.json +1 -1
- package/src/evm-operations.ts +386 -0
- package/src/index.ts +93 -5
- package/src/signing-messages.ts +16 -0
- package/src/types.ts +96 -0
- package/src/utils.ts +41 -2
- package/src/vault-utils.ts +89 -0
- package/types/constants.d.ts +17 -0
- package/types/derive.d.ts +0 -1
- package/types/evm-operations.d.ts +4 -0
- package/types/index.d.ts +5 -1
- package/types/signing-messages.d.ts +4 -0
- package/types/types.d.ts +87 -0
- package/types/utils.d.ts +1 -0
- package/types/vault-utils.d.ts +34 -0
- package/utils/emblem-site-metadata.json +1 -1
- package/utils/sdk-metadata.json +1 -1
package/docs/index.html
CHANGED
|
@@ -122,7 +122,8 @@
|
|
|
122
122
|
let contract = window.curatedContracts[index];
|
|
123
123
|
let template = contract.generateCreateTemplate(contract)
|
|
124
124
|
template.chainId = 1
|
|
125
|
-
|
|
125
|
+
let toAddr = document.getElementById('toAddress').value;
|
|
126
|
+
template.toAddress = toAddr ? toAddr : document.getElementById('ethAddress').value;
|
|
126
127
|
template.fromAddress = document.getElementById('ethAddress').value;
|
|
127
128
|
window.contract = contract;
|
|
128
129
|
window.template = template;
|
|
@@ -204,6 +205,8 @@
|
|
|
204
205
|
</script>
|
|
205
206
|
<input type="text" id="ethAddress" placeholder="Enter ETH Address" oninput="localStorage.setItem('ethAddress', this.value)" value="">
|
|
206
207
|
|
|
208
|
+
<input type="text" id="toAddress" placeholder="To Address (optional)" oninput="localStorage.setItem('toAddress', this.value)" value="">
|
|
209
|
+
|
|
207
210
|
<input type="checkbox" id="hideNotMintableCheckbox" checked onchange="generateDropdown(window.curatedContracts)">
|
|
208
211
|
<label for="hideNotMintableCheckbox">Hide Not Mintable</label>
|
|
209
212
|
|
|
@@ -243,6 +246,9 @@
|
|
|
243
246
|
<a href="steps.html" target="_blank">Full Create / Mint : Demo</a>
|
|
244
247
|
</div>
|
|
245
248
|
|
|
246
|
-
<script>
|
|
249
|
+
<script>
|
|
250
|
+
document.getElementById('ethAddress').value = localStorage.getItem('ethAddress');
|
|
251
|
+
document.getElementById('toAddress').value = localStorage.getItem('toAddress');
|
|
252
|
+
</script>
|
|
247
253
|
</body>
|
|
248
254
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emblem-vault-sdk",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.2",
|
|
4
4
|
"description": "Emblem Vault Software Development Kit",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"test:browser": "JEST_ENV=browser jest",
|
|
13
13
|
"test:all": "npm run test:node && npm run test:browser",
|
|
14
14
|
"build": "tsc",
|
|
15
|
-
"postbuild": "node ./script/version.js && mv dist/index.d.ts types/index.d.ts && mv dist/types.d.ts types/types.d.ts && mv dist/utils.d.ts types/utils.d.ts && mv dist/derive.d.ts types/derive.d.ts",
|
|
15
|
+
"postbuild": "node ./script/version.js && mv dist/index.d.ts types/index.d.ts && mv dist/types.d.ts types/types.d.ts && mv dist/utils.d.ts types/utils.d.ts && mv dist/derive.d.ts types/derive.d.ts && mv dist/constants.d.ts types/constants.d.ts && mv dist/evm-operations.d.ts types/evm-operations.d.ts && mv dist/signing-messages.d.ts types/signing-messages.d.ts && mv dist/vault-utils.d.ts types/vault-utils.d.ts",
|
|
16
16
|
"bundle": "npm run build && browserify dist/index.js -o dist/bundle.js && cp dist/bundle.js docs/bundle.js",
|
|
17
17
|
"watch": "onchange 'src/*.ts' -- npm run bundle"
|
|
18
18
|
},
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@types/crypto-js": "^4.2.2",
|
|
25
25
|
"@types/jest": "^29.5.11",
|
|
26
26
|
"browserify": "^17.0.0",
|
|
27
|
+
"esmify": "^2.1.1",
|
|
27
28
|
"jest": "^29.7.0",
|
|
28
29
|
"jest-environment-jsdom": "^29.7.0",
|
|
29
30
|
"jest-puppeteer": "^9.0.2",
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"bitcore-mnemonic": "^10.0.23",
|
|
45
46
|
"crypto-js": "^4.2.0",
|
|
46
47
|
"ethereumjs-util": "^7.1.5",
|
|
48
|
+
"ethers": "^5.7.2",
|
|
47
49
|
"install": "^0.13.0",
|
|
48
50
|
"npm": "^10.4.0",
|
|
49
51
|
"sats-connect": "^1.4.1",
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Supported Chains
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
export const ETHEREUM_MAINNET_CHAIN_ID = 1;
|
|
6
|
+
export const POLYGON_MAINNET_CHAIN_ID = 137;
|
|
7
|
+
export const SOLANA_CHAIN_IDENTIFIER = 'solana';
|
|
8
|
+
|
|
9
|
+
export type ChainType = 'evm' | 'solana';
|
|
10
|
+
|
|
11
|
+
export interface ChainConfig {
|
|
12
|
+
type: ChainType;
|
|
13
|
+
name: string;
|
|
14
|
+
handlerContract?: string;
|
|
15
|
+
unvaultingContract?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const SUPPORTED_CHAINS: Record<number | string, ChainConfig> = {
|
|
19
|
+
[ETHEREUM_MAINNET_CHAIN_ID]: {
|
|
20
|
+
type: 'evm',
|
|
21
|
+
name: 'Ethereum Mainnet',
|
|
22
|
+
handlerContract: '0x23859b51117dbFBcdEf5b757028B18d7759a4460',
|
|
23
|
+
unvaultingContract: '0x214C964bBd3640971E111d3a994CbB89b296a9ad',
|
|
24
|
+
},
|
|
25
|
+
[POLYGON_MAINNET_CHAIN_ID]: {
|
|
26
|
+
type: 'evm',
|
|
27
|
+
name: 'Polygon Mainnet',
|
|
28
|
+
handlerContract: '0x23859b51117dbFBcdEf5b757028B18d7759a4460',
|
|
29
|
+
unvaultingContract: '0x214C964bBd3640971E111d3a994CbB89b296a9ad',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Contract Addresses (default to Ethereum mainnet)
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
export const HANDLER_CONTRACT_ADDRESS = '0x23859b51117dbFBcdEf5b757028B18d7759a4460';
|
|
38
|
+
export const UNVAULTING_DIAMOND_ADDRESS = '0x214C964bBd3640971E111d3a994CbB89b296a9ad';
|
|
39
|
+
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// API Endpoints
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
export const EMBLEM_API_V2 = 'https://v2.emblemvault.io';
|
|
46
|
+
export const EMBLEM_API_2 = 'https://api2.emblemvault.io';
|
|
47
|
+
export const TORUS_SIGNER_API = 'https://tor-us-signer-coval.vercel.app';
|
|
@@ -668143,7 +668143,7 @@
|
|
|
668143
668143
|
"price": 1040.9323103174047,
|
|
668144
668144
|
"lastPrice": 1314.8618656640904
|
|
668145
668145
|
},
|
|
668146
|
-
"video": "https://arweave.net/Fb7BMNWJQwNCrRxdw_dbMC01t6iSVUytBtg5LziOOlI/2o51ir_video.mp4
|
|
668146
|
+
"video": "https://arweave.net/Fb7BMNWJQwNCrRxdw_dbMC01t6iSVUytBtg5LziOOlI/2o51ir_video.mp4",
|
|
668147
668147
|
"projectProtocol": "Counterparty"
|
|
668148
668148
|
},
|
|
668149
668149
|
"DGAPEPE": {
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EvmSigner,
|
|
3
|
+
EmblemVaultClient,
|
|
4
|
+
ProgressCallback,
|
|
5
|
+
MintResult,
|
|
6
|
+
ClaimResult,
|
|
7
|
+
RemoteMintSignature,
|
|
8
|
+
RemoteUnvaultSignature,
|
|
9
|
+
MetaData,
|
|
10
|
+
SdkContext,
|
|
11
|
+
} from './types';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
ZERO_ADDRESS,
|
|
15
|
+
EMBLEM_API_2,
|
|
16
|
+
TORUS_SIGNER_API,
|
|
17
|
+
} from './constants';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
buildMintMessage,
|
|
21
|
+
buildClaimMessage,
|
|
22
|
+
buildUnvaultMessage,
|
|
23
|
+
buildDeleteMessage,
|
|
24
|
+
} from './signing-messages';
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
getHandlerContractAddress,
|
|
28
|
+
getUnvaultingContractAddress,
|
|
29
|
+
} from './vault-utils';
|
|
30
|
+
|
|
31
|
+
import { fetchData, parseBigIntValue } from './utils';
|
|
32
|
+
|
|
33
|
+
const EVM_RPC_URLS: Record<number, string> = {
|
|
34
|
+
1: 'https://eth.llamarpc.com',
|
|
35
|
+
137: 'https://polygon-rpc.com',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function getEvmRpcUrl(chainId: number): string {
|
|
39
|
+
return EVM_RPC_URLS[chainId] || EVM_RPC_URLS[1];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function performMintEvm(
|
|
43
|
+
ctx: SdkContext,
|
|
44
|
+
client: EmblemVaultClient,
|
|
45
|
+
tokenId: string,
|
|
46
|
+
chainId: number,
|
|
47
|
+
callback?: ProgressCallback
|
|
48
|
+
): Promise<MintResult> {
|
|
49
|
+
callback?.('Initializing EVM signer...');
|
|
50
|
+
const { ethers } = await import('ethers');
|
|
51
|
+
const provider = new ethers.providers.JsonRpcProvider(getEvmRpcUrl(chainId));
|
|
52
|
+
const wallet = await client.toEthersWallet(provider);
|
|
53
|
+
wallet.setChainId?.(chainId);
|
|
54
|
+
|
|
55
|
+
callback?.('Signing mint request...');
|
|
56
|
+
const mintMessage = buildMintMessage(tokenId);
|
|
57
|
+
const mintRequestSig = await wallet.signMessage(mintMessage);
|
|
58
|
+
|
|
59
|
+
callback?.('Getting remote mint signature...');
|
|
60
|
+
const remoteMintSig = await requestRemoteMintSignature(ctx, tokenId, mintRequestSig, chainId);
|
|
61
|
+
|
|
62
|
+
callback?.('Submitting mint transaction...');
|
|
63
|
+
const txHash = await submitMintTransaction(wallet, remoteMintSig, chainId, callback);
|
|
64
|
+
|
|
65
|
+
callback?.('Mint complete!', { txHash });
|
|
66
|
+
return { txHash, tokenId, chainId };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function performClaimEvm(
|
|
70
|
+
ctx: SdkContext,
|
|
71
|
+
client: EmblemVaultClient,
|
|
72
|
+
tokenId: string,
|
|
73
|
+
chainId: number,
|
|
74
|
+
metadata: MetaData,
|
|
75
|
+
claimIdentifier: string,
|
|
76
|
+
vaultIsV2: boolean,
|
|
77
|
+
needsOnChainUnvault: boolean,
|
|
78
|
+
callback?: ProgressCallback
|
|
79
|
+
): Promise<ClaimResult> {
|
|
80
|
+
callback?.('Initializing EVM signer...');
|
|
81
|
+
const { ethers } = await import('ethers');
|
|
82
|
+
const provider = new ethers.providers.JsonRpcProvider(getEvmRpcUrl(chainId));
|
|
83
|
+
const wallet = await client.toEthersWallet(provider);
|
|
84
|
+
wallet.setChainId?.(chainId);
|
|
85
|
+
|
|
86
|
+
// Determine if vault needs on-chain claim/unvault before getting keys
|
|
87
|
+
// For minted vaults (live=true, status=unclaimed), we must claim on-chain first
|
|
88
|
+
const isMinted = metadata.live === true || metadata.status === 'unclaimed';
|
|
89
|
+
const isAlreadyClaimed = metadata.status === 'claimed' || Boolean(metadata.claimedBy);
|
|
90
|
+
|
|
91
|
+
if (isMinted && !isAlreadyClaimed) {
|
|
92
|
+
if (needsOnChainUnvault) {
|
|
93
|
+
// V2 vault: use unvaultWithSignedPrice
|
|
94
|
+
await performOnChainUnvault(
|
|
95
|
+
ctx,
|
|
96
|
+
wallet,
|
|
97
|
+
tokenId,
|
|
98
|
+
claimIdentifier,
|
|
99
|
+
metadata.collectionAddress,
|
|
100
|
+
chainId,
|
|
101
|
+
callback
|
|
102
|
+
);
|
|
103
|
+
} else {
|
|
104
|
+
// Non-V2 vault: use handler's claim function
|
|
105
|
+
const targetContractAddress = metadata.targetContract?.[chainId] || metadata.collectionAddress;
|
|
106
|
+
if (targetContractAddress) {
|
|
107
|
+
callback?.('Performing on-chain claim...');
|
|
108
|
+
await performLegacyClaim(wallet, targetContractAddress, claimIdentifier, chainId, callback);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
callback?.('Signing claim message...');
|
|
114
|
+
const claimMessage = buildClaimMessage(claimIdentifier, vaultIsV2);
|
|
115
|
+
const signature = await wallet.signMessage(claimMessage);
|
|
116
|
+
|
|
117
|
+
callback?.('Requesting claim token...');
|
|
118
|
+
const jwt = await requestClaimToken(tokenId, signature, chainId);
|
|
119
|
+
|
|
120
|
+
callback?.('Requesting remote key...');
|
|
121
|
+
const decryptionKeys = await ctx.requestRemoteKey(tokenId, jwt, callback);
|
|
122
|
+
if (!decryptionKeys) {
|
|
123
|
+
throw new Error('Failed to get decryption key');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
callback?.('Decrypting vault keys...');
|
|
127
|
+
return await ctx.decryptVaultKeys(tokenId, decryptionKeys, callback);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function deleteVaultEvm(
|
|
131
|
+
client: EmblemVaultClient,
|
|
132
|
+
tokenId: string,
|
|
133
|
+
chainId: number,
|
|
134
|
+
callback?: ProgressCallback
|
|
135
|
+
): Promise<boolean> {
|
|
136
|
+
callback?.('Initializing EVM signer...');
|
|
137
|
+
const wallet = await client.toEthersWallet(null);
|
|
138
|
+
wallet.setChainId?.(chainId);
|
|
139
|
+
|
|
140
|
+
callback?.('Signing delete message...');
|
|
141
|
+
const deleteMessage = buildDeleteMessage(tokenId);
|
|
142
|
+
const signature = await wallet.signMessage(deleteMessage);
|
|
143
|
+
|
|
144
|
+
callback?.('Deleting vault...');
|
|
145
|
+
const response = await fetch(`${EMBLEM_API_2}/v2/delete`, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: {
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
service: 'evmetadata',
|
|
150
|
+
},
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
tokenId,
|
|
153
|
+
signature,
|
|
154
|
+
chainId: chainId.toString(),
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
const errorData = await response.json().catch(() => ({}));
|
|
160
|
+
throw new Error(errorData.message || 'Failed to delete vault');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
callback?.('Vault deleted successfully');
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function requestRemoteMintSignature(
|
|
168
|
+
ctx: SdkContext,
|
|
169
|
+
tokenId: string,
|
|
170
|
+
signature: string,
|
|
171
|
+
chainId: number
|
|
172
|
+
): Promise<RemoteMintSignature> {
|
|
173
|
+
const url = `${ctx.baseUrl}/mint-curated`;
|
|
174
|
+
const remoteMintResponse = await fetchData(
|
|
175
|
+
url,
|
|
176
|
+
ctx.apiKey,
|
|
177
|
+
'POST',
|
|
178
|
+
{
|
|
179
|
+
method: 'buyWithSignedPrice',
|
|
180
|
+
tokenId,
|
|
181
|
+
signature,
|
|
182
|
+
chainId: chainId.toString(),
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (remoteMintResponse.error) {
|
|
187
|
+
throw new Error(remoteMintResponse.error);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return remoteMintResponse as RemoteMintSignature;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function submitMintTransaction(
|
|
194
|
+
wallet: EvmSigner,
|
|
195
|
+
remoteMintSig: RemoteMintSignature,
|
|
196
|
+
chainId: number,
|
|
197
|
+
callback?: ProgressCallback
|
|
198
|
+
): Promise<string> {
|
|
199
|
+
const { ethers } = await import('ethers');
|
|
200
|
+
|
|
201
|
+
const handlerAddress = getHandlerContractAddress(chainId);
|
|
202
|
+
const priceBigInt = parseBigIntValue(remoteMintSig._price);
|
|
203
|
+
|
|
204
|
+
const iface = new ethers.utils.Interface([
|
|
205
|
+
'function buyWithSignedPrice(address _nftAddress, address _payment, uint256 _price, address _to, uint256 _tokenId, uint256 _nonce, bytes _signature, bytes serialNumber, uint256 _amount) payable'
|
|
206
|
+
]);
|
|
207
|
+
|
|
208
|
+
const data = iface.encodeFunctionData('buyWithSignedPrice', [
|
|
209
|
+
remoteMintSig._nftAddress,
|
|
210
|
+
ZERO_ADDRESS,
|
|
211
|
+
priceBigInt,
|
|
212
|
+
remoteMintSig._to,
|
|
213
|
+
parseBigIntValue(remoteMintSig._tokenId),
|
|
214
|
+
parseBigIntValue(remoteMintSig._nonce),
|
|
215
|
+
remoteMintSig._signature,
|
|
216
|
+
remoteMintSig.serialNumber,
|
|
217
|
+
BigInt(1),
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
const tx = await wallet.sendTransaction({
|
|
221
|
+
to: handlerAddress,
|
|
222
|
+
data,
|
|
223
|
+
value: priceBigInt,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
callback?.('Waiting for confirmation...', { txHash: tx.hash });
|
|
227
|
+
await tx.wait();
|
|
228
|
+
|
|
229
|
+
return tx.hash;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function performOnChainUnvault(
|
|
233
|
+
ctx: SdkContext,
|
|
234
|
+
wallet: EvmSigner,
|
|
235
|
+
tokenId: string,
|
|
236
|
+
claimIdentifier: string,
|
|
237
|
+
nftAddress: string | undefined,
|
|
238
|
+
chainId: number,
|
|
239
|
+
callback?: ProgressCallback
|
|
240
|
+
): Promise<void> {
|
|
241
|
+
callback?.('Signing unvault request...');
|
|
242
|
+
// Sign with tokenId to match server expectation (server verifies "Unvault: " + req.body.tokenId)
|
|
243
|
+
const unvaultMessage = buildUnvaultMessage(tokenId);
|
|
244
|
+
const unvaultSignature = await wallet.signMessage(unvaultMessage);
|
|
245
|
+
|
|
246
|
+
callback?.('Requesting remote unvault signature...');
|
|
247
|
+
const remoteUnvaultSig = await requestRemoteUnvaultSignature(ctx, tokenId, unvaultSignature, chainId);
|
|
248
|
+
|
|
249
|
+
callback?.('Submitting on-chain unvault transaction...');
|
|
250
|
+
await submitUnvaultTransaction(wallet, remoteUnvaultSig, nftAddress, chainId, callback);
|
|
251
|
+
|
|
252
|
+
callback?.('On-chain unvault complete, retrieving keys...');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Perform legacy claim by calling the handler contract's claim function
|
|
257
|
+
* This is for non-V2 vaults that don't use signed price unvaulting
|
|
258
|
+
*/
|
|
259
|
+
async function performLegacyClaim(
|
|
260
|
+
wallet: EvmSigner,
|
|
261
|
+
targetContractAddress: string,
|
|
262
|
+
tokenId: string,
|
|
263
|
+
chainId: number,
|
|
264
|
+
callback?: ProgressCallback
|
|
265
|
+
): Promise<void> {
|
|
266
|
+
const { ethers } = await import('ethers');
|
|
267
|
+
|
|
268
|
+
// Handler contract ABI for claim function
|
|
269
|
+
const HANDLER_CLAIM_ABI = [
|
|
270
|
+
'function claim(address _nftAddress, uint256 _tokenId) external'
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
const handlerAddress = getHandlerContractAddress(chainId);
|
|
274
|
+
const iface = new ethers.utils.Interface(HANDLER_CLAIM_ABI);
|
|
275
|
+
|
|
276
|
+
const data = iface.encodeFunctionData('claim', [
|
|
277
|
+
targetContractAddress,
|
|
278
|
+
BigInt(tokenId),
|
|
279
|
+
]);
|
|
280
|
+
|
|
281
|
+
callback?.('Submitting claim transaction...');
|
|
282
|
+
const tx = await wallet.sendTransaction({
|
|
283
|
+
to: handlerAddress,
|
|
284
|
+
data,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
callback?.('Waiting for claim confirmation...', { txHash: tx.hash });
|
|
288
|
+
await tx.wait();
|
|
289
|
+
|
|
290
|
+
callback?.('On-chain claim complete!');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function requestRemoteUnvaultSignature(
|
|
294
|
+
ctx: SdkContext,
|
|
295
|
+
tokenId: string,
|
|
296
|
+
signature: string,
|
|
297
|
+
chainId: number
|
|
298
|
+
): Promise<RemoteUnvaultSignature> {
|
|
299
|
+
const response = await fetch(`${ctx.baseUrl}/unvault-curated`, {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: { 'Content-Type': 'application/json' },
|
|
302
|
+
body: JSON.stringify({
|
|
303
|
+
method: 'unvaultWithSignedPrice',
|
|
304
|
+
tokenId,
|
|
305
|
+
signature,
|
|
306
|
+
chainId: chainId.toString(),
|
|
307
|
+
}),
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const remoteUnvaultSig = await response.json() as RemoteUnvaultSignature;
|
|
311
|
+
|
|
312
|
+
if (!remoteUnvaultSig?.success) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
remoteUnvaultSig?.msg || remoteUnvaultSig?.err || 'Failed to get remote unvault signature'
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return remoteUnvaultSig;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function submitUnvaultTransaction(
|
|
322
|
+
wallet: EvmSigner,
|
|
323
|
+
remoteUnvaultSig: RemoteUnvaultSignature,
|
|
324
|
+
nftAddress: string | undefined,
|
|
325
|
+
chainId: number,
|
|
326
|
+
callback?: ProgressCallback
|
|
327
|
+
): Promise<string> {
|
|
328
|
+
const { ethers } = await import('ethers');
|
|
329
|
+
|
|
330
|
+
const unvaultingAddress = getUnvaultingContractAddress(chainId);
|
|
331
|
+
const nftAddressToUse = nftAddress || remoteUnvaultSig._nftAddress;
|
|
332
|
+
const tokenId = parseBigIntValue(remoteUnvaultSig._tokenId);
|
|
333
|
+
const nonce = parseBigIntValue(remoteUnvaultSig._nonce);
|
|
334
|
+
const price = parseBigIntValue(remoteUnvaultSig._price);
|
|
335
|
+
const timestamp = parseBigIntValue(remoteUnvaultSig._timestamp);
|
|
336
|
+
|
|
337
|
+
const iface = new ethers.utils.Interface([
|
|
338
|
+
'function unvaultWithSignedPrice(address _nftAddress, uint256 _tokenId, uint256 _nonce, address _payment, uint256 _price, bytes _signature, uint256 _timestamp) payable'
|
|
339
|
+
]);
|
|
340
|
+
|
|
341
|
+
const data = iface.encodeFunctionData('unvaultWithSignedPrice', [
|
|
342
|
+
nftAddressToUse,
|
|
343
|
+
tokenId,
|
|
344
|
+
nonce,
|
|
345
|
+
ZERO_ADDRESS,
|
|
346
|
+
price,
|
|
347
|
+
remoteUnvaultSig._signature,
|
|
348
|
+
timestamp,
|
|
349
|
+
]);
|
|
350
|
+
|
|
351
|
+
const tx = await wallet.sendTransaction({
|
|
352
|
+
to: unvaultingAddress,
|
|
353
|
+
data,
|
|
354
|
+
value: price,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
callback?.('Waiting for unvault confirmation...', { txHash: tx.hash });
|
|
358
|
+
await tx.wait();
|
|
359
|
+
|
|
360
|
+
return tx.hash;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function requestClaimToken(
|
|
364
|
+
tokenId: string,
|
|
365
|
+
signature: string,
|
|
366
|
+
chainId: number
|
|
367
|
+
): Promise<{ token: string }> {
|
|
368
|
+
const response = await fetch(`${TORUS_SIGNER_API}/sign`, {
|
|
369
|
+
method: 'POST',
|
|
370
|
+
headers: {
|
|
371
|
+
'Content-Type': 'application/json',
|
|
372
|
+
chainid: chainId.toString(),
|
|
373
|
+
},
|
|
374
|
+
body: JSON.stringify({ signature, tokenId }),
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const jwt = await response.json();
|
|
378
|
+
|
|
379
|
+
if (!jwt || jwt.success === false) {
|
|
380
|
+
throw new Error(
|
|
381
|
+
jwt?.debug ? `Claim failed: ${JSON.stringify(jwt.debug)}` : 'Failed to get claim token'
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return jwt;
|
|
386
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { BigNumber } from '@ethersproject/bignumber'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import type {
|
|
3
|
+
Collection,
|
|
4
|
+
CuratedCollectionsResponse,
|
|
5
|
+
MetaData,
|
|
6
|
+
Ownership,
|
|
7
|
+
Vault,
|
|
8
|
+
ProgressCallback,
|
|
9
|
+
MintResult,
|
|
10
|
+
ClaimResult,
|
|
11
|
+
EmblemVaultClient,
|
|
12
|
+
SdkContext,
|
|
13
|
+
} from './types';
|
|
14
|
+
import { NFT_DATA, checkContentType, decryptKeys, fetchData, generateTemplate, genericGuard, getHandlerContract, getLegacyContract, getQuoteContractObject, getSatsConnectAddress, getTorusKeys, metadataAllProjects, metadataObj2Arr, signPSBT, templateGuard } from './utils';
|
|
5
15
|
import { generateTaprootAddressFromMnemonic, getPsbtTxnSize } from './derive';
|
|
16
|
+
import {
|
|
17
|
+
ETHEREUM_MAINNET_CHAIN_ID,
|
|
18
|
+
} from './constants';
|
|
19
|
+
import {
|
|
20
|
+
getChainConfig,
|
|
21
|
+
isV2Vault,
|
|
22
|
+
requiresOnChainUnvault,
|
|
23
|
+
getClaimIdentifier,
|
|
24
|
+
} from './vault-utils';
|
|
25
|
+
import { performMintEvm, performClaimEvm, deleteVaultEvm } from './evm-operations';
|
|
26
|
+
|
|
6
27
|
const SDK_VERSION = '__SDK_VERSION__';
|
|
7
28
|
|
|
8
29
|
class EmblemVaultSDK {
|
|
@@ -76,7 +97,7 @@ class EmblemVaultSDK {
|
|
|
76
97
|
item[key] = template[key];
|
|
77
98
|
});
|
|
78
99
|
// Return a new object that combines the properties of the item and the template
|
|
79
|
-
|
|
100
|
+
return { ...item, ...template };
|
|
80
101
|
return item;
|
|
81
102
|
});
|
|
82
103
|
return data
|
|
@@ -94,7 +115,14 @@ class EmblemVaultSDK {
|
|
|
94
115
|
let url = `${this.baseUrl}/create-curated`;
|
|
95
116
|
if (callback) { callback(`creating Vault for user`, template.toAddress)}
|
|
96
117
|
let vaultCreationResponse = await fetchData(url, this.apiKey, 'POST', template);
|
|
97
|
-
|
|
118
|
+
|
|
119
|
+
if (vaultCreationResponse?.err) {
|
|
120
|
+
if (callback) callback(`error creating Vault: ${vaultCreationResponse.msg}`)
|
|
121
|
+
return vaultCreationResponse
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const { tokenId } = vaultCreationResponse.data
|
|
125
|
+
if (callback) { callback(`created Vault ${tokenId}`)}
|
|
98
126
|
return vaultCreationResponse.data
|
|
99
127
|
}
|
|
100
128
|
|
|
@@ -480,6 +508,66 @@ class EmblemVaultSDK {
|
|
|
480
508
|
return results;
|
|
481
509
|
}
|
|
482
510
|
|
|
511
|
+
// ========================================================================
|
|
512
|
+
// Remote Signer Methods (EmblemVaultClient) - Multi-Chain Support
|
|
513
|
+
// ========================================================================
|
|
514
|
+
|
|
515
|
+
private getSdkContext(): SdkContext {
|
|
516
|
+
return {
|
|
517
|
+
apiKey: this.apiKey,
|
|
518
|
+
baseUrl: this.baseUrl,
|
|
519
|
+
v3Url: this.v3Url,
|
|
520
|
+
sigUrl: this.sigUrl,
|
|
521
|
+
fetchMetadata: this.fetchMetadata.bind(this),
|
|
522
|
+
requestRemoteKey: this.requestRemoteKey.bind(this),
|
|
523
|
+
decryptVaultKeys: this.decryptVaultKeys.bind(this),
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
async performMintChainWithClient(
|
|
528
|
+
client: EmblemVaultClient,
|
|
529
|
+
tokenId: string,
|
|
530
|
+
chainId: number | 'solana' = ETHEREUM_MAINNET_CHAIN_ID,
|
|
531
|
+
callback?: ProgressCallback
|
|
532
|
+
): Promise<MintResult> {
|
|
533
|
+
genericGuard(tokenId, "string", "tokenId");
|
|
534
|
+
getChainConfig(chainId);
|
|
535
|
+
return performMintEvm(this.getSdkContext(), client, tokenId, chainId as number, callback);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async performClaimChainWithClient(
|
|
539
|
+
client: EmblemVaultClient,
|
|
540
|
+
tokenId: string,
|
|
541
|
+
chainId: number | 'solana' = ETHEREUM_MAINNET_CHAIN_ID,
|
|
542
|
+
callback?: ProgressCallback
|
|
543
|
+
): Promise<ClaimResult> {
|
|
544
|
+
genericGuard(tokenId, "string", "tokenId");
|
|
545
|
+
getChainConfig(chainId);
|
|
546
|
+
|
|
547
|
+
callback?.('Fetching vault metadata...');
|
|
548
|
+
const metadata = await this.fetchMetadata(tokenId, callback);
|
|
549
|
+
const claimIdentifier = getClaimIdentifier(metadata);
|
|
550
|
+
|
|
551
|
+
// Check if this is a V2 vault by checking the deployment type (matches VaultActions.vue logic)
|
|
552
|
+
// metadata[chainId]?.type?.endsWith('V2') indicates a diamond/V2 contract
|
|
553
|
+
const deploymentInfo = (metadata as Record<string, unknown>)?.[chainId as number] as { type?: string } | undefined;
|
|
554
|
+
const vaultIsV2 = Boolean(deploymentInfo?.type?.endsWith?.('V2'));
|
|
555
|
+
const needsOnChainUnvault = requiresOnChainUnvault(metadata) && vaultIsV2;
|
|
556
|
+
|
|
557
|
+
return performClaimEvm(this.getSdkContext(), client, tokenId, chainId as number, metadata, claimIdentifier, vaultIsV2, needsOnChainUnvault, callback);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async deleteVaultWithClient(
|
|
561
|
+
client: EmblemVaultClient,
|
|
562
|
+
tokenId: string,
|
|
563
|
+
chainId: number | 'solana' = ETHEREUM_MAINNET_CHAIN_ID,
|
|
564
|
+
callback?: ProgressCallback
|
|
565
|
+
): Promise<boolean> {
|
|
566
|
+
genericGuard(tokenId, "string", "tokenId");
|
|
567
|
+
getChainConfig(chainId);
|
|
568
|
+
return deleteVaultEvm(client, tokenId, chainId as number, callback);
|
|
569
|
+
}
|
|
570
|
+
|
|
483
571
|
// BTC
|
|
484
572
|
|
|
485
573
|
async sweepVaultUsingPhrase(phrase: string, satsPerByte: number = 20, broadcast: boolean = false) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function buildMintMessage(tokenId: string): string {
|
|
2
|
+
return `Curated Minting: ${tokenId}`;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function buildClaimMessage(identifier: string, isV2Vault: boolean): string {
|
|
6
|
+
const prefix = isV2Vault ? 'Unvault' : 'Claim';
|
|
7
|
+
return `${prefix}: ${identifier}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function buildUnvaultMessage(identifier: string): string {
|
|
11
|
+
return `Unvault: ${identifier}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function buildDeleteMessage(tokenId: string): string {
|
|
15
|
+
return `Delete: ${tokenId}`;
|
|
16
|
+
}
|