@pbgtoken/rwa-contract 1.0.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.
@@ -0,0 +1,27 @@
1
+ import { type Address } from "@helios-lang/ledger";
2
+ import { type BlockfrostV0Client } from "@helios-lang/tx-utils";
3
+ import { type PricesProvider } from "./PricesProvider";
4
+ import { type TokenizedAccountProvider } from "./TokenizedAccountProvider";
5
+ /**
6
+ * Constructs a CardanoWallet-specific implementation of TokenizedAccountClient.
7
+ *
8
+ * @param provider
9
+ * Must be a mainnet provider!
10
+ * TODO: generalize to allow any Cardano provider
11
+ *
12
+ * @param address
13
+ * Must be a Shelley-era address on mainnet
14
+ *
15
+ * @param prices
16
+ * A provider for USD/ADA prices etc.
17
+ *
18
+ * @throws
19
+ * If the `provider` isn't for mainnet.
20
+ *
21
+ * @throws
22
+ * If the `address` isn't a Shelley-era address.
23
+ *
24
+ * @returns
25
+ * A CardanoWallet-specific implementation of TokenizedAccountClient.
26
+ */
27
+ export declare function makeCardanoWalletProvider(provider: BlockfrostV0Client, address: Address, prices: PricesProvider): TokenizedAccountProvider;
@@ -0,0 +1,13 @@
1
+ export interface PricesProvider {
2
+ /**
3
+ * @param asset
4
+ * eg. ADA
5
+ *
6
+ * @param currency
7
+ * eg. USD
8
+ *
9
+ * @returns
10
+ * A number `currency` for one `asset`
11
+ */
12
+ getSpotPrice(asset: string, currency: string): Promise<number>;
13
+ }
@@ -0,0 +1,51 @@
1
+ import { StrictType } from "@helios-lang/contract-utils";
2
+ import { MintingPolicyHash } from "@helios-lang/ledger";
3
+ import { ReadonlyCardanoClient } from "@helios-lang/tx-utils";
4
+ declare const castDatum: (config: import("@helios-lang/contract-utils").CastConfig) => import("@helios-lang/contract-utils").Cast<{
5
+ current_supply: bigint;
6
+ supply_after_last_mint: bigint;
7
+ transfer_id_before_last_mint: number[];
8
+ type: string;
9
+ account: number[];
10
+ name: string;
11
+ description: string;
12
+ decimals: bigint;
13
+ ticker: string;
14
+ url: string;
15
+ logo: string;
16
+ }, {
17
+ current_supply: import("@helios-lang/codec-utils").IntLike;
18
+ supply_after_last_mint: import("@helios-lang/codec-utils").IntLike;
19
+ transfer_id_before_last_mint: number[];
20
+ type: string;
21
+ account: number[];
22
+ name: string;
23
+ description: string;
24
+ decimals: import("@helios-lang/codec-utils").IntLike;
25
+ ticker: string;
26
+ url: string;
27
+ logo: string;
28
+ }>;
29
+ export type RWADatum = StrictType<typeof castDatum>;
30
+ /**
31
+ * Export the interface, not the class itself.
32
+ *
33
+ * This approach allows as to add/remove/rename private properties without breaking compatibiliy.
34
+ * This also prevents the use of `instanceof`, which is an evil operator.
35
+ */
36
+ export interface RWADatumProvider {
37
+ fetch(policy: MintingPolicyHash): Promise<RWADatum>;
38
+ }
39
+ /**
40
+ * Constructs a RWADatumProvider instance.
41
+ *
42
+ * @param client
43
+ * The Cardano provider, initialized to the correct network
44
+ *
45
+ * TODO: generalize to be able to use any Cardano provider
46
+ *
47
+ * @returns
48
+ * A RWADatumProvider instance.
49
+ */
50
+ export declare function makeRWADatumProvider(provider: ReadonlyCardanoClient): RWADatumProvider;
51
+ export {};
@@ -0,0 +1,21 @@
1
+ import { type TransferID } from "./TransferID";
2
+ export interface TokenizedAccountProvider {
3
+ /**
4
+ * @returns
5
+ * USD value of account holdings
6
+ */
7
+ balance: Promise<number>;
8
+ /**
9
+ * @returns
10
+ * All historical transfer IDs, sorted from earliest to latest
11
+ */
12
+ transferHistory: Promise<TransferID[]>;
13
+ /**
14
+ * @param transfers
15
+ * Only aggregate the deposits/withdrawals of these transfers (so not of all transfers)
16
+ *
17
+ * @returns
18
+ * USD value aggregate of deposits (positive) and withdrawals (negative)
19
+ */
20
+ deposits(transfers: TransferID[]): Promise<number>;
21
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Hex encoded transfer ID bytes
3
+ *
4
+ * A hex string is easier to work with than a list of numbers
5
+ */
6
+ export type TransferID = string;
7
+ /**
8
+ * @param transfers
9
+ * List of sorted transfers, earliest first
10
+ *
11
+ * @param after
12
+ * Not including this transfer
13
+ *
14
+ * @returns
15
+ * Filtered list of transfers
16
+ */
17
+ export declare function filterTransfersAfter(transfers: TransferID[], after: TransferID): TransferID[];
@@ -0,0 +1,5 @@
1
+ export { makeCardanoWalletProvider } from "./CardanoWalletProvider";
2
+ export { type PricesProvider } from "./PricesProvider";
3
+ export { makeRWADatumProvider, type RWADatumProvider } from "./RWADatumProvider";
4
+ export { type TokenizedAccountProvider } from "./TokenizedAccountProvider";
5
+ export { filterTransfersAfter, type TransferID } from "./TransferID";
package/dist/index.js ADDED
@@ -0,0 +1,473 @@
1
+ // src/CardanoWalletProvider.ts
2
+ import { addValues, makeTxId, makeValue } from "@helios-lang/ledger";
3
+ import { getAssetClassInfo } from "@helios-lang/tx-utils";
4
+
5
+ // src/retryExpBackoff.ts
6
+ async function retryExpBackoff(callback, config) {
7
+ let attempt = 1;
8
+ let lastError = void 0;
9
+ while (attempt <= config.maxRetries) {
10
+ try {
11
+ const res = await callback();
12
+ return res;
13
+ } catch (e) {
14
+ lastError = e;
15
+ }
16
+ attempt++;
17
+ if (attempt <= config.maxRetries) {
18
+ await new Promise((resolve) => {
19
+ setTimeout(resolve, config.delay * Math.pow(2, attempt - 1));
20
+ });
21
+ }
22
+ }
23
+ throw lastError;
24
+ }
25
+
26
+ // src/CardanoWalletProvider.ts
27
+ function makeCardanoWalletProvider(provider, address, prices) {
28
+ if (!provider.isMainnet()) {
29
+ throw new Error("not a mainnet Cardano provider");
30
+ }
31
+ if (address.era != "Shelley") {
32
+ throw new Error("not a Shelley era address");
33
+ }
34
+ if (!address.mainnet) {
35
+ throw new Error("not a mainnet Cardano wallet address");
36
+ }
37
+ return new CardanoWalletProviderImpl(provider, address, prices);
38
+ }
39
+ var CardanoWalletProviderImpl = class {
40
+ provider;
41
+ address;
42
+ prices;
43
+ /**
44
+ * @param provider
45
+ * The Cardano client
46
+ *
47
+ * @param address
48
+ * The mainnet address for which to fetch the balance and transactions.
49
+ *
50
+ * @param prices
51
+ */
52
+ constructor(provider, address, prices) {
53
+ this.provider = provider;
54
+ this.address = address;
55
+ this.prices = prices;
56
+ }
57
+ /**
58
+ * @returns
59
+ * The USD total value of the Cardano wallet
60
+ */
61
+ get balance() {
62
+ return (async () => {
63
+ const utxos = await this.provider.getUtxos(this.address);
64
+ const v = addValues(utxos);
65
+ const ada = await this.aggregateValue(v);
66
+ const adaPrice = await this.prices.getSpotPrice("ADA", "USD");
67
+ return ada * adaPrice;
68
+ })();
69
+ }
70
+ /**
71
+ * @returns
72
+ * A list of hex encoded transaction IDs
73
+ */
74
+ get transferHistory() {
75
+ return (async () => {
76
+ const txInfos = await this.provider.getAddressTxs(this.address);
77
+ txInfos.sort((a, b) => {
78
+ return a.blockTime - b.blockTime;
79
+ });
80
+ return txInfos.map((txInfo) => txInfo.id.toHex());
81
+ })();
82
+ }
83
+ /**
84
+ * @param transfers
85
+ * Sum over the given transfers only
86
+ *
87
+ * @returns
88
+ * USD value of deposits (positive) and withdrawals (negative)
89
+ */
90
+ async deposits(transfers) {
91
+ let txs = [];
92
+ for (let i = 0; i < transfers.length; i++) {
93
+ const id = transfers[i];
94
+ const tx = await retryExpBackoff(
95
+ async () => {
96
+ return await this.provider.getTxInfo(makeTxId(id));
97
+ },
98
+ {
99
+ delay: 1e3,
100
+ maxRetries: 3
101
+ }
102
+ );
103
+ txs.push(tx);
104
+ }
105
+ const depositValue = txs.reduce((value, tx) => {
106
+ value = tx.inputs.reduce((v, input) => {
107
+ if (input.address.isEqual(this.address)) {
108
+ return v.subtract(input.value);
109
+ } else {
110
+ return v;
111
+ }
112
+ }, value);
113
+ value = tx.outputs.reduce((v, output) => {
114
+ if (output.address.isEqual(this.address)) {
115
+ return v.add(output.value);
116
+ } else {
117
+ return v;
118
+ }
119
+ }, value);
120
+ return value;
121
+ }, makeValue(0n));
122
+ return await this.aggregateValue(depositValue);
123
+ }
124
+ /**
125
+ * CNTs are Cardano Native Tokens
126
+ *
127
+ * @param value
128
+ * Lovelace + CNTs
129
+ *
130
+ * @returns
131
+ * A number of USD
132
+ */
133
+ async aggregateValue(value) {
134
+ let ada = Number(value.lovelace) / 1e6;
135
+ for (let ac of value.assets.assetClasses) {
136
+ try {
137
+ const { ticker, decimals } = await getAssetClassInfo(
138
+ this.provider,
139
+ ac
140
+ );
141
+ try {
142
+ const tickerPrice = await this.prices.getSpotPrice(
143
+ ticker,
144
+ "ADA"
145
+ );
146
+ const qty = value.assets.getAssetClassQuantity(ac);
147
+ ada += Number(qty) / Math.pow(10, decimals) * tickerPrice;
148
+ } catch (e) {
149
+ console.error(
150
+ `${ticker} price not yet available (${e.message})`
151
+ );
152
+ }
153
+ } catch (e) {
154
+ console.error(
155
+ `${ac.toString()} doesn't have CIP26 metadata, ignoring (${e.message})`
156
+ );
157
+ }
158
+ }
159
+ const adaPrice = await this.prices.getSpotPrice("ADA", "USD");
160
+ return ada * adaPrice;
161
+ }
162
+ };
163
+
164
+ // src/RWADatumProvider.ts
165
+ import { bytesToHex } from "@helios-lang/codec-utils";
166
+ import { makeShelleyAddress, makeValidatorHash } from "@helios-lang/ledger";
167
+ import { expectDefined } from "@helios-lang/type-utils";
168
+
169
+ // src/validators/index.ts
170
+ import { makeCast, makeUserFunc } from "@helios-lang/contract-utils";
171
+ var tokenized_account = {
172
+ $name: "tokenized_account",
173
+ $purpose: "mixed",
174
+ $currentScriptIndex: 0,
175
+ $sourceCode: `mixed tokenized_account
176
+
177
+ import { get_current_input, tx } from ScriptContext
178
+
179
+ struct State {
180
+ current_supply: Int "current_supply" // smaller or equal to supply_after_last_mint
181
+ supply_after_last_mint: Int "supply_after_last_mint"
182
+ transfer_id_before_last_mint: ByteArray "transfer_id_before_last_mint" // this is hash, because it might contain data that shouldn't be public
183
+
184
+ type: String "type" // eg. CardanoWallet or Private
185
+ account: ByteArray "account" // if type==Private -> this is a hash of the actual account type + address
186
+
187
+ name: String "name"
188
+ description: String "description"
189
+ decimals: Int "decimals" // can't change
190
+ ticker: String "ticker" // can't change
191
+ url: String "url"
192
+ logo: String "logo"
193
+ }
194
+
195
+ enum Cip68Extra {
196
+ Unused
197
+ }
198
+
199
+ enum Metadata {
200
+ Cip68 {
201
+ state: State
202
+ version: Int
203
+ extra: Cip68Extra
204
+ }
205
+
206
+ func state(self) -> State {
207
+ self.switch{
208
+ x: Cip68 => x.state
209
+ }
210
+ }
211
+ }
212
+
213
+ struct Redeemer {
214
+ total_reserves: Real // usually a USD value, 6 decimal places is enough
215
+ reserves_change: Real
216
+ latest_transfer_id: ByteArray
217
+ }
218
+
219
+ const SEED_ID = TxOutputId::new(TxId::new(#), 0)
220
+ const ORACLE_KEYS = []PubKeyHash{}
221
+ const INITIAL_PRICE: Real = 0.001 // 1000 USD per token -> 0.001 USD per microtoken
222
+ const TYPE = "Private"
223
+ const ACCOUNT = #
224
+ const TICKER = "USDT"
225
+ const NAME = TICKER + " RWA"
226
+ const DESCRIPTION = TICKER + " RWA operated by PBG"
227
+ const DECIMALS = 6
228
+ const URL = "https://www.pbg.io"
229
+ const LOGO = "https://assets.pbg.io/usdt_bridge.png"
230
+
231
+ const ticker_bytes = TICKER.encode_utf8()
232
+ const user_token_name = Cip67::fungible_token_label + ticker_bytes
233
+ const ref_token_name = Cip67::reference_token_label + ticker_bytes
234
+
235
+ const own_hash = Scripts::tokenized_account
236
+ const own_mph = MintingPolicyHash::from_script_hash(own_hash)
237
+ const own_address = Address::new(
238
+ SpendingCredential::new_validator(
239
+ ValidatorHash::from_script_hash(own_hash)
240
+ ),
241
+ Option[StakingCredential]::None
242
+ )
243
+
244
+ const ref_token_asset_class = AssetClass::new(own_mph, ref_token_name)
245
+ const user_token_asset_class = AssetClass::new(own_mph, user_token_name)
246
+
247
+ func validate_initialization() -> () {
248
+ assert(tx.inputs.any((input: TxInput) -> {
249
+ input.output_id == SEED_ID
250
+ }), "seed UTXO not spent")
251
+
252
+ ref_utxo = tx.outputs.find((output: TxOutput) -> {
253
+ output.address == own_address
254
+ && output.value.get_safe(ref_token_asset_class) == 1
255
+ })
256
+
257
+ metadata = ref_utxo.datum.inline.as_strictly[Metadata]
258
+
259
+ assert(metadata == Metadata::Cip68{
260
+ State{
261
+ current_supply: 0,
262
+ supply_after_last_mint: 0,
263
+ transfer_id_before_last_mint: #,
264
+
265
+ type: TYPE,
266
+ account: ACCOUNT,
267
+
268
+ name: NAME,
269
+ description: DESCRIPTION,
270
+ decimals: DECIMALS,
271
+ ticker: TICKER,
272
+ url: URL,
273
+ logo: LOGO
274
+ },
275
+ 1,
276
+ Cip68Extra::Unused
277
+ }, "metadata not initialized correctly")
278
+ }
279
+
280
+ func signed_by_quorum() -> Bool {
281
+ n_signers = ORACLE_KEYS.fold((n_signers: Int, key: PubKeyHash) -> {
282
+ n_signers + tx.is_signed_by(key).to_int()
283
+ }, 0)
284
+
285
+ n_signers > (ORACLE_KEYS.length/2)
286
+ }
287
+
288
+ func calc_token_price(R: Real, Delta: Real, current_supply: Int, supply_after_last_mint: Int) -> Real {
289
+ if (current_supply == 0 || supply_after_last_mint == 0) {
290
+ INITIAL_PRICE
291
+ } else {
292
+ p_last = (R - Delta)/supply_after_last_mint
293
+ p_curr = R / current_supply
294
+
295
+ if (p_last < p_curr) {
296
+ p_last
297
+ } else {
298
+ p_curr
299
+ }
300
+ }
301
+ }
302
+
303
+ func validate_state_change(redeemer: Redeemer, input: TxInput) -> () {
304
+ state0 = input.datum.inline.as[Metadata].state()
305
+
306
+ output = tx.outputs.find((output: TxOutput) -> {
307
+ output.address == own_address
308
+ && output.value.get_safe(ref_token_asset_class) > 0
309
+ })
310
+
311
+ state1 = output.datum.inline.as_strictly[Metadata].state()
312
+
313
+ // ensure constant metadata fields don't change
314
+ assert(state1.type == state0.type, "type not constant")
315
+ assert(state1.account == state0.account, "account not constant")
316
+ assert(state1.ticker == state0.ticker, "metadata ticker not constant")
317
+ assert(state1.decimals == state0.decimals, "metadata decimals not constant")
318
+
319
+ n = tx.minted.get_safe(user_token_asset_class)
320
+ N0 = state0.current_supply
321
+ N1 = state1.current_supply
322
+
323
+ assert((N1 - N0) == n, "current token supply not updated correctly")
324
+
325
+ if (n > 0) {
326
+ Redeemer{R, Delta, latest_transfer_id} = redeemer
327
+
328
+ p = calc_token_price(R, Delta, N0, state0.supply_after_last_mint)
329
+
330
+ assert(N1*p <= R, "too many tokens minted")
331
+ assert(state1.transfer_id_before_last_mint == latest_transfer_id, "transfer_id_before_last_mint not updated correctly")
332
+ assert(state1.supply_after_last_mint == N1, "supply_after_last_mint not updated correctly")
333
+ assert(signed_by_quorum(), "not signed by simple quorum of oracles")
334
+ } else {
335
+ assert(state1.supply_after_last_mint == state0.supply_after_last_mint, "supply_after_last_mint can't change during burn")
336
+ assert(state1.transfer_id_before_last_mint == state0.transfer_id_before_last_mint, "transfer_id_before_last_mint can't change during burn")
337
+ }
338
+ }
339
+
340
+ func main(args: MixedArgs) -> () {
341
+ args.switch{
342
+ s: Spending => {
343
+ redeemer = s.redeemer.as[Redeemer]
344
+
345
+ utxo = get_current_input()
346
+
347
+ tokens = utxo.value.get_policy(own_mph)
348
+
349
+ if (tokens.is_empty()) {
350
+ // UTXOs that don't contain any tokens from current policy can always be spent.
351
+ // This can be used to remove garbage.
352
+ ()
353
+ } else {
354
+ (name, _) = tokens.head
355
+
356
+ if (name == ref_token_name) {
357
+ validate_state_change(redeemer, utxo)
358
+ } else {
359
+ error("unexpected token name")
360
+ }
361
+ }
362
+ },
363
+ Other => {
364
+ tokens = tx.minted.get_policy(own_mph)
365
+
366
+ (name, qty) = tokens.head
367
+ tail = tokens.tail
368
+
369
+ assert(tail.is_empty(), "only one token kind can be minted or burned")
370
+
371
+ if (name == user_token_name && qty != 0) {
372
+ // metadata token must be spent, which triggers Spending witness
373
+ assert(tx.inputs.any((input: TxInput) -> {
374
+ input.address == own_address
375
+ && input.value.get_safe(ref_token_asset_class) > 0
376
+ }), "ref token not spent")
377
+ } else if (qty == 1 && (name == ref_token_name)) {
378
+ validate_initialization()
379
+ } else {
380
+ error("invalid minted tokens")
381
+ }
382
+ }
383
+ }
384
+ }`,
385
+ $dependencies: [],
386
+ $hashDependencies: [],
387
+ $dependsOnOwnHash: true,
388
+ $Redeemer: (config) => makeCast({ "kind": "enum", "id": "__helios__mixedargs", "name": "MixedArgs", "variantTypes": [{ "kind": "variant", "name": "Other", "id": "__helios__mixedargs__other", "tag": 0, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }, { "kind": "variant", "name": "Spending", "id": "__helios__mixedargs__spending", "tag": 1, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }] }, config),
389
+ $Datum: (config) => makeCast({ "kind": "internal", "name": "Data" }, config),
390
+ $types: {
391
+ State: (config) => makeCast({ "kind": "struct", "format": "map", "id": "__module__tokenized_account__State[]", "name": "State", "fieldTypes": [{ "name": "current_supply", "type": { "kind": "internal", "name": "Int" }, "key": "current_supply" }, { "name": "supply_after_last_mint", "type": { "kind": "internal", "name": "Int" }, "key": "supply_after_last_mint" }, { "name": "transfer_id_before_last_mint", "type": { "kind": "internal", "name": "ByteArray" }, "key": "transfer_id_before_last_mint" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] }, config),
392
+ Cip68Extra: (config) => makeCast({ "kind": "enum", "name": "Cip68Extra", "id": "__module__tokenized_account__Cip68Extra[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__tokenized_account__Cip68Extra[]__Unused", "name": "Unused", "fieldTypes": [] }] }, config),
393
+ Metadata: (config) => makeCast({ "kind": "enum", "name": "Metadata", "id": "__module__tokenized_account__Metadata[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__tokenized_account__Metadata[]__Cip68", "name": "Cip68", "fieldTypes": [{ "name": "state", "type": { "kind": "struct", "format": "map", "id": "__module__tokenized_account__State[]", "name": "State", "fieldTypes": [{ "name": "current_supply", "type": { "kind": "internal", "name": "Int" }, "key": "current_supply" }, { "name": "supply_after_last_mint", "type": { "kind": "internal", "name": "Int" }, "key": "supply_after_last_mint" }, { "name": "transfer_id_before_last_mint", "type": { "kind": "internal", "name": "ByteArray" }, "key": "transfer_id_before_last_mint" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] } }, { "name": "version", "type": { "kind": "internal", "name": "Int" } }, { "name": "extra", "type": { "kind": "enum", "name": "Cip68Extra", "id": "__module__tokenized_account__Cip68Extra[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__tokenized_account__Cip68Extra[]__Unused", "name": "Unused", "fieldTypes": [] }] } }] }] }, config),
394
+ Redeemer: (config) => makeCast({ "kind": "struct", "format": "list", "id": "__module__tokenized_account__Redeemer[]", "name": "Redeemer", "fieldTypes": [{ "name": "total_reserves", "type": { "kind": "internal", "name": "Real" } }, { "name": "reserves_change", "type": { "kind": "internal", "name": "Real" } }, { "name": "latest_transfer_id", "type": { "kind": "internal", "name": "ByteArray" } }] }, config)
395
+ },
396
+ $functions: {
397
+ "Metadata::state": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "Metadata::state", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [{ "name": "self", "isOptional": false, "type": { "kind": "enum", "name": "Metadata", "id": "__module__tokenized_account__Metadata[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__tokenized_account__Metadata[]__Cip68", "name": "Cip68", "fieldTypes": [{ "name": "state", "type": { "kind": "struct", "format": "map", "id": "__module__tokenized_account__State[]", "name": "State", "fieldTypes": [{ "name": "current_supply", "type": { "kind": "internal", "name": "Int" }, "key": "current_supply" }, { "name": "supply_after_last_mint", "type": { "kind": "internal", "name": "Int" }, "key": "supply_after_last_mint" }, { "name": "transfer_id_before_last_mint", "type": { "kind": "internal", "name": "ByteArray" }, "key": "transfer_id_before_last_mint" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] } }, { "name": "version", "type": { "kind": "internal", "name": "Int" } }, { "name": "extra", "type": { "kind": "enum", "name": "Cip68Extra", "id": "__module__tokenized_account__Cip68Extra[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__tokenized_account__Cip68Extra[]__Unused", "name": "Unused", "fieldTypes": [] }] } }] }] } }], "returns": { "kind": "struct", "format": "map", "id": "__module__tokenized_account__State[]", "name": "State", "fieldTypes": [{ "name": "current_supply", "type": { "kind": "internal", "name": "Int" }, "key": "current_supply" }, { "name": "supply_after_last_mint", "type": { "kind": "internal", "name": "Int" }, "key": "supply_after_last_mint" }, { "name": "transfer_id_before_last_mint", "type": { "kind": "internal", "name": "ByteArray" }, "key": "transfer_id_before_last_mint" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] } }, castConfig: config, validatorIndices: __validatorIndices }),
398
+ "SEED_ID": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "SEED_ID", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "TxOutputId" } }, castConfig: config, validatorIndices: __validatorIndices }),
399
+ "ORACLE_KEYS": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ORACLE_KEYS", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "list", "itemType": { "kind": "internal", "name": "PubKeyHash" } } }, castConfig: config, validatorIndices: __validatorIndices }),
400
+ "INITIAL_PRICE": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "INITIAL_PRICE", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "Real" } }, castConfig: config, validatorIndices: __validatorIndices }),
401
+ "TYPE": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "TYPE", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
402
+ "ACCOUNT": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ACCOUNT", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
403
+ "TICKER": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "TICKER", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
404
+ "NAME": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "NAME", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
405
+ "DESCRIPTION": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "DESCRIPTION", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
406
+ "DECIMALS": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "DECIMALS", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "Int" } }, castConfig: config, validatorIndices: __validatorIndices }),
407
+ "URL": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "URL", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
408
+ "LOGO": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "LOGO", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
409
+ "ticker_bytes": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ticker_bytes", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
410
+ "user_token_name": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "user_token_name", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
411
+ "ref_token_name": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ref_token_name", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
412
+ "own_hash": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "own_hash", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ScriptHash" } }, castConfig: config, validatorIndices: __validatorIndices }),
413
+ "own_mph": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "own_mph", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "MintingPolicyHash" } }, castConfig: config, validatorIndices: __validatorIndices }),
414
+ "own_address": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "own_address", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "Address" } }, castConfig: config, validatorIndices: __validatorIndices }),
415
+ "ref_token_asset_class": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ref_token_asset_class", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "AssetClass" } }, castConfig: config, validatorIndices: __validatorIndices }),
416
+ "user_token_asset_class": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "user_token_asset_class", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "AssetClass" } }, castConfig: config, validatorIndices: __validatorIndices }),
417
+ "validate_initialization": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "validate_initialization", "requiresCurrentScript": false, "requiresScriptContext": true, "arguments": [] }, castConfig: config, validatorIndices: __validatorIndices }),
418
+ "signed_by_quorum": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "signed_by_quorum", "requiresCurrentScript": false, "requiresScriptContext": true, "arguments": [], "returns": { "kind": "internal", "name": "Bool" } }, castConfig: config, validatorIndices: __validatorIndices }),
419
+ "calc_token_price": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "calc_token_price", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [{ "name": "R", "isOptional": false, "type": { "kind": "internal", "name": "Real" } }, { "name": "Delta", "isOptional": false, "type": { "kind": "internal", "name": "Real" } }, { "name": "current_supply", "isOptional": false, "type": { "kind": "internal", "name": "Int" } }, { "name": "supply_after_last_mint", "isOptional": false, "type": { "kind": "internal", "name": "Int" } }], "returns": { "kind": "internal", "name": "Real" } }, castConfig: config, validatorIndices: __validatorIndices }),
420
+ "validate_state_change": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "validate_state_change", "requiresCurrentScript": false, "requiresScriptContext": true, "arguments": [{ "name": "redeemer", "isOptional": false, "type": { "kind": "struct", "format": "list", "id": "__module__tokenized_account__Redeemer[]", "name": "Redeemer", "fieldTypes": [{ "name": "total_reserves", "type": { "kind": "internal", "name": "Real" } }, { "name": "reserves_change", "type": { "kind": "internal", "name": "Real" } }, { "name": "latest_transfer_id", "type": { "kind": "internal", "name": "ByteArray" } }] } }, { "name": "input", "isOptional": false, "type": { "kind": "internal", "name": "TxInput" } }] }, castConfig: config, validatorIndices: __validatorIndices }),
421
+ "main": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "main", "arguments": [{ "name": "$datum", "isOptional": true, "type": { "kind": "internal", "name": "Data" } }, { "name": "args", "isOptional": false, "type": { "kind": "enum", "id": "__helios__mixedargs", "name": "MixedArgs", "variantTypes": [{ "kind": "variant", "name": "Other", "id": "__helios__mixedargs__other", "tag": 0, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }, { "kind": "variant", "name": "Spending", "id": "__helios__mixedargs__spending", "tag": 1, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }] } }], "requiresCurrentScript": false, "requiresScriptContext": true }, castConfig: config, validatorIndices: __validatorIndices })
422
+ }
423
+ };
424
+ var __validatorIndices = { "tokenized_account": 0 };
425
+
426
+ // src/RWADatumProvider.ts
427
+ var castMetadata = tokenized_account.$types.Metadata;
428
+ var castDatum = tokenized_account.$types.State;
429
+ function makeRWADatumProvider(provider) {
430
+ return new RWADatumProviderImpl(provider);
431
+ }
432
+ var RWADatumProviderImpl = class {
433
+ provider;
434
+ constructor(provider) {
435
+ this.provider = provider;
436
+ }
437
+ async fetch(policy) {
438
+ const utxo = await this.fetchMetadataUTXO(policy);
439
+ const metadata = castMetadata({ isMainnet: this.isMainnet }).fromUplcData(expectDefined(utxo.datum?.data, "metadata UTXO doesn't have datum"));
440
+ return metadata.Cip68.state;
441
+ }
442
+ get isMainnet() {
443
+ return this.provider.isMainnet();
444
+ }
445
+ async fetchMetadataUTXO(policy) {
446
+ const vh = makeValidatorHash(policy.bytes);
447
+ const addr = makeShelleyAddress(this.isMainnet, vh);
448
+ let utxos = await this.provider.getUtxos(addr);
449
+ utxos = utxos.filter(
450
+ (utxo) => utxo.value.assets.assets.some(
451
+ ([mph, tokens]) => mph.isEqual(policy) && tokens.some(
452
+ ([tokenName, qty]) => bytesToHex(tokenName.slice(0, 4)) == "000643b0" && qty == 1n
453
+ )
454
+ )
455
+ );
456
+ return expectDefined(utxos[0], "expected 1 metadata UTXO");
457
+ }
458
+ };
459
+
460
+ // src/TransferID.ts
461
+ function filterTransfersAfter(transfers, after) {
462
+ const cutOff = transfers.findIndex((id) => id == after);
463
+ if (cutOff != -1) {
464
+ return transfers.slice(cutOff + 1);
465
+ } else {
466
+ return transfers;
467
+ }
468
+ }
469
+ export {
470
+ filterTransfersAfter,
471
+ makeCardanoWalletProvider,
472
+ makeRWADatumProvider
473
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Internal helper for retrying network requests
3
+ *
4
+ * @param callback
5
+ * @param config
6
+ * delay is a number in milliseconds
7
+ *
8
+ * @returns
9
+ */
10
+ export declare function retryExpBackoff<T>(callback: () => Promise<T>, config: {
11
+ delay: number;
12
+ maxRetries: number;
13
+ }): Promise<T>;
@@ -0,0 +1,2 @@
1
+ import { type MintingPolicyHash } from "@helios-lang/ledger";
2
+ export declare function makeMetadataAssetClass(mph: MintingPolicyHash, ticker: string): import("@helios-lang/ledger").AssetClass<unknown>;