diff --git a/UPGRADING.md b/UPGRADING.md
index 9dee4337..798416ae 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -1,6 +1,5 @@
# Upgrading
-Moving a project to `@wharfkit/*` 4.0.0 is covered on the website:
+Moving a project to `@wharfkit/*` 4.0.1 is covered on the website:
-- [Upgrading to WharfKit 4.0.0](https://wharfkit.com/docs/releases/upgrading-to-4-0-0), the procedure, the version each package comes from, and a checklist.
-- [Wharf 4.0.0 release notes](https://wharfkit.com/docs/releases/4-0-0), what each package gained.
+- [Wharf 4.0.1](https://wharfkit.com/docs/releases/4-0-1#upgrading), the upgrade steps, the changes to check in your code, and what each package gained.
diff --git a/bun.lock b/bun.lock
index c7ac3e36..dd8a84f3 100644
--- a/bun.lock
+++ b/bun.lock
@@ -32,7 +32,7 @@
},
"packages/abicache": {
"name": "@wharfkit/abicache",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"@wharfkit/signing-request": "workspace:*",
@@ -44,7 +44,7 @@
},
"packages/account": {
"name": "@wharfkit/account",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"@wharfkit/common": "workspace:*",
@@ -60,7 +60,7 @@
},
"packages/account-creation-plugin-anchor": {
"name": "@wharfkit/account-creation-plugin-anchor",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -71,7 +71,7 @@
},
"packages/account-creation-plugin-jungle4": {
"name": "@wharfkit/account-creation-plugin-jungle4",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -82,7 +82,7 @@
},
"packages/account-creation-plugin-metamask": {
"name": "@wharfkit/account-creation-plugin-metamask",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@metamask/providers": "^17.0.0",
"@wharfkit/session": "workspace:*",
@@ -94,7 +94,7 @@
},
"packages/actionstream": {
"name": "@wharfkit/actionstream",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
},
@@ -105,7 +105,7 @@
},
"packages/antelope": {
"name": "@wharfkit/antelope",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@noble/curves": "^2.2.0",
"@noble/hashes": "^2.2.0",
@@ -115,7 +115,7 @@
},
"packages/atomicassets": {
"name": "@wharfkit/atomicassets",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"@wharfkit/common": "workspace:*",
@@ -128,7 +128,7 @@
},
"packages/bundle": {
"name": "@wharfkit/bundle",
- "version": "4.0.0",
+ "version": "4.0.1",
"devDependencies": {
"@wharfkit/account": "workspace:*",
"@wharfkit/antelope": "workspace:*",
@@ -154,7 +154,7 @@
},
"packages/cli": {
"name": "@wharfkit/cli",
- "version": "4.0.0",
+ "version": "4.0.1",
"bin": {
"wharfkit": "./lib/cli.js",
},
@@ -178,7 +178,7 @@
},
"packages/common": {
"name": "@wharfkit/common",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
},
@@ -189,7 +189,7 @@
},
"packages/conformance": {
"name": "@wharfkit/conformance",
- "version": "4.0.0",
+ "version": "4.0.1",
"devDependencies": {
"@greymass/vert": "^3.0.0",
"@types/bun": "^1.0.4",
@@ -200,7 +200,7 @@
},
"packages/contract": {
"name": "@wharfkit/contract",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/abicache": "workspace:*",
"@wharfkit/antelope": "workspace:*",
@@ -214,7 +214,7 @@
},
"packages/hyperion": {
"name": "@wharfkit/hyperion",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
},
@@ -224,7 +224,7 @@
},
"packages/light-api": {
"name": "@wharfkit/light-api",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
},
@@ -234,7 +234,7 @@
},
"packages/mock-data": {
"name": "@wharfkit/mock-data",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -243,7 +243,7 @@
},
"packages/msigs": {
"name": "@wharfkit/msigs",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
},
@@ -253,7 +253,7 @@
},
"packages/protocol-esr": {
"name": "@wharfkit/protocol-esr",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@greymass/buoy": "catalog:",
"@wharfkit/sealed-messages": "workspace:*",
@@ -272,7 +272,7 @@
},
"packages/protocol-scatter": {
"name": "@wharfkit/protocol-scatter",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -287,7 +287,7 @@
},
"packages/resources": {
"name": "@wharfkit/resources",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"bn.js": "catalog:",
@@ -299,7 +299,7 @@
},
"packages/roborovski": {
"name": "@wharfkit/roborovski",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
},
@@ -309,7 +309,7 @@
},
"packages/sealed-messages": {
"name": "@wharfkit/sealed-messages",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@greymass/miniaes": "^1.0.0",
"@wharfkit/antelope": "workspace:*",
@@ -320,7 +320,7 @@
},
"packages/session": {
"name": "@wharfkit/session",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/abicache": "workspace:*",
"@wharfkit/antelope": "workspace:*",
@@ -337,14 +337,14 @@
},
"packages/signing-request": {
"name": "@wharfkit/signing-request",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
},
},
"packages/svelte-components": {
"name": "@wharfkit/svelte-components",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@lucide/svelte": "^0.516.0",
"@melt-ui/svelte": "^0.86.6",
@@ -391,7 +391,7 @@
},
"packages/token": {
"name": "@wharfkit/token",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"@wharfkit/contract": "workspace:*",
@@ -402,7 +402,7 @@
},
"packages/transact-plugin-autocorrect": {
"name": "@wharfkit/transact-plugin-autocorrect",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/resources": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -415,7 +415,7 @@
},
"packages/transact-plugin-cosigner": {
"name": "@wharfkit/transact-plugin-cosigner",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -427,7 +427,7 @@
},
"packages/transact-plugin-explorerlink": {
"name": "@wharfkit/transact-plugin-explorerlink",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -439,7 +439,7 @@
},
"packages/transact-plugin-finality-callback": {
"name": "@wharfkit/transact-plugin-finality-callback",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -450,7 +450,7 @@
},
"packages/transact-plugin-finality-checker": {
"name": "@wharfkit/transact-plugin-finality-checker",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -462,7 +462,7 @@
},
"packages/transact-plugin-mock": {
"name": "@wharfkit/transact-plugin-mock",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -474,7 +474,7 @@
},
"packages/transact-plugin-msig-propose": {
"name": "@wharfkit/transact-plugin-msig-propose",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -486,7 +486,7 @@
},
"packages/transact-plugin-resource-provider": {
"name": "@wharfkit/transact-plugin-resource-provider",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/resources": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -501,7 +501,7 @@
},
"packages/wallet-plugin-anchor": {
"name": "@wharfkit/wallet-plugin-anchor",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@greymass/buoy": "catalog:",
"@wharfkit/antelope": "workspace:*",
@@ -522,7 +522,7 @@
},
"packages/wallet-plugin-cleos": {
"name": "@wharfkit/wallet-plugin-cleos",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -533,7 +533,7 @@
},
"packages/wallet-plugin-cloudwallet": {
"name": "@wharfkit/wallet-plugin-cloudwallet",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -544,7 +544,7 @@
},
"packages/wallet-plugin-gatewallet": {
"name": "@wharfkit/wallet-plugin-gatewallet",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/protocol-scatter": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -557,7 +557,7 @@
},
"packages/wallet-plugin-imtoken": {
"name": "@wharfkit/wallet-plugin-imtoken",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/protocol-scatter": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -570,7 +570,7 @@
},
"packages/wallet-plugin-metamask": {
"name": "@wharfkit/wallet-plugin-metamask",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@metamask/providers": "^17.0.0",
"@wharfkit/session": "workspace:*",
@@ -582,7 +582,7 @@
},
"packages/wallet-plugin-mimic": {
"name": "@wharfkit/wallet-plugin-mimic",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -593,7 +593,7 @@
},
"packages/wallet-plugin-mock": {
"name": "@wharfkit/wallet-plugin-mock",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -605,7 +605,7 @@
},
"packages/wallet-plugin-paycash": {
"name": "@wharfkit/wallet-plugin-paycash",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/protocol-esr": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -617,7 +617,7 @@
},
"packages/wallet-plugin-privatekey": {
"name": "@wharfkit/wallet-plugin-privatekey",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -629,7 +629,7 @@
},
"packages/wallet-plugin-scatter": {
"name": "@wharfkit/wallet-plugin-scatter",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/protocol-scatter": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -641,7 +641,7 @@
},
"packages/wallet-plugin-tacklebox": {
"name": "@wharfkit/wallet-plugin-tacklebox",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@greymass/buoy": "catalog:",
"@wharfkit/protocol-esr": "workspace:*",
@@ -656,7 +656,7 @@
},
"packages/wallet-plugin-tokenpocket": {
"name": "@wharfkit/wallet-plugin-tokenpocket",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/protocol-scatter": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -668,7 +668,7 @@
},
"packages/wallet-plugin-web-authenticator": {
"name": "@wharfkit/wallet-plugin-web-authenticator",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@greymass/buoy": "catalog:",
"@wharfkit/antelope": "workspace:*",
@@ -687,7 +687,7 @@
},
"packages/web-renderer": {
"name": "@wharfkit/web-renderer",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/session": "workspace:*",
},
@@ -738,7 +738,7 @@
},
"packages/web-ui": {
"name": "@wharfkit/web-ui",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/common": "workspace:*",
"@wharfkit/session": "workspace:*",
@@ -770,7 +770,7 @@
},
"packages/webauthn": {
"name": "@wharfkit/webauthn",
- "version": "4.0.0",
+ "version": "4.0.1",
"dependencies": {
"@wharfkit/antelope": "workspace:*",
"cborg": "^4.5.8",
diff --git a/package.json b/package.json
index 071b2b0c..ed40c7f5 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "wharfkit-js",
"private": true,
- "version": "4.0.0",
+ "version": "4.0.1",
"workspaces": {
"packages": [
"packages/*"
diff --git a/packages/abicache/package.json b/packages/abicache/package.json
index d26e717e..a7dcab34 100644
--- a/packages/abicache/package.json
+++ b/packages/abicache/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/abicache",
"description": "ABI Caching Mechanism for use in Session and Contract Kits",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/account-creation-plugin-anchor/package.json b/packages/account-creation-plugin-anchor/package.json
index 5bc7408e..9bd353e0 100644
--- a/packages/account-creation-plugin-anchor/package.json
+++ b/packages/account-creation-plugin-anchor/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/account-creation-plugin-anchor",
"description": "An account creation plugin using the Greymass account creation service",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/account-creation-plugin-anchor",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/account-creation-plugin-jungle4/package.json b/packages/account-creation-plugin-jungle4/package.json
index 2e0f7c54..78834099 100644
--- a/packages/account-creation-plugin-jungle4/package.json
+++ b/packages/account-creation-plugin-jungle4/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/account-creation-plugin-jungle4",
"description": "Plugin to create a Jungle4 Testnet acccount.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/account-creation-plugin-jungle4",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/account-creation-plugin-metamask/package.json b/packages/account-creation-plugin-metamask/package.json
index d8d72629..1eaae9ce 100644
--- a/packages/account-creation-plugin-metamask/package.json
+++ b/packages/account-creation-plugin-metamask/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/account-creation-plugin-metamask",
"description": "A MetaMask plugin to create EOS accounts using Metamask public keys.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/account-creation-plugin-metamask",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/account/package.json b/packages/account/package.json
index 5aaab8cc..12df116e 100644
--- a/packages/account/package.json
+++ b/packages/account/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/account",
"description": "Account kit for Wharf Kit",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/docs/account-kit",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/actionstream/package.json b/packages/actionstream/package.json
index 8d7b7d89..d43cfeaa 100644
--- a/packages/actionstream/package.json
+++ b/packages/actionstream/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/actionstream",
"description": "Client library for subscribing to Roborovski action streams",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/antelope/package.json b/packages/antelope/package.json
index a3959b06..2044e163 100644
--- a/packages/antelope/package.json
+++ b/packages/antelope/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/antelope",
"description": "Library for working with Antelope powered blockchains.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/docs/antelope",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/antelope/src/chain/public-key.ts b/packages/antelope/src/chain/public-key.ts
index 222d7b23..0eb94ef8 100644
--- a/packages/antelope/src/chain/public-key.ts
+++ b/packages/antelope/src/chain/public-key.ts
@@ -37,9 +37,10 @@ export class PublicKey implements ABISerializableObject {
const size = type === KeyType.K1 || type === KeyType.R1 ? 33 : undefined
const data = Base58.decodeRipemd160Check(parts[2], size, type)
return new PublicKey(type, data)
- } else if (value.length >= 50) {
- // Legacy EOS key
- const data = Base58.decodeRipemd160Check(value.slice(-50))
+ } else if (value.startsWith('EOS') || value.length >= 50) {
+ // Legacy EOS key; leading zero bytes shorten the encoding, e.g. the null key
+ const encoded = value.startsWith('EOS') ? value.slice(3) : value.slice(-50)
+ const data = Base58.decodeRipemd160Check(encoded, 33)
return new PublicKey(KeyType.K1, data)
} else {
throw new Error('Invalid public key string')
diff --git a/packages/antelope/src/chain/struct.ts b/packages/antelope/src/chain/struct.ts
index d0ec5d92..67ea80e4 100644
--- a/packages/antelope/src/chain/struct.ts
+++ b/packages/antelope/src/chain/struct.ts
@@ -14,6 +14,14 @@ export interface StructConstructor extends ABISerializableConstructor {
structFields: ABIField[]
}
+function fieldModifiers(field: ABIField) {
+ if (typeof field.type === 'string') {
+ const resolved = new ABI.ResolvedType(field.type)
+ return {isOptional: resolved.isOptional, isExtension: resolved.isExtension}
+ }
+ return {isOptional: !!field.optional, isExtension: !!field.extension}
+}
+
export class Struct implements ABISerializableObject {
static abiName = '__struct'
static abiFields: ABIField[]
@@ -50,12 +58,10 @@ export class Struct implements ABISerializableObject {
constructor(object: any) {
const self = this.constructor as typeof Struct
for (const field of self.structFields) {
- const isOptional =
- typeof field.type === 'string'
- ? new ABI.ResolvedType(String(field.type)).isOptional
- : field.optional
+ const {isOptional, isExtension} = fieldModifiers(field)
const value = object[field.name]
if (isOptional && !value) continue
+ if (isExtension && value === undefined) continue
this[field.name] = value
}
}
@@ -84,6 +90,7 @@ export class Struct implements ABISerializableObject {
const rv: any = {}
for (const field of self.structFields) {
if (field.optional && !this[field.name]) continue
+ if (this[field.name] === undefined && fieldModifiers(field).isExtension) continue
rv[field.name] = this[field.name]
}
return rv
diff --git a/packages/antelope/src/serializer/decoder.ts b/packages/antelope/src/serializer/decoder.ts
index bfbf6e16..50820f66 100644
--- a/packages/antelope/src/serializer/decoder.ts
+++ b/packages/antelope/src/serializer/decoder.ts
@@ -198,6 +198,9 @@ function decodeBinary(type: ABI.ResolvedType, decoder: ABIDecoder, ctx: Decoding
}
const rv: any = {}
for (const field of fields) {
+ if (field.type.isExtension && !ctx.strictExtensions && !decoder.canRead()) {
+ continue
+ }
ctx.codingPath.push({field: field.name, type: field.type})
rv[field.name] = decodeBinary(field.type, decoder, ctx)
ctx.codingPath.pop()
@@ -283,8 +286,16 @@ function decodeObject(value: any, type: ABI.ResolvedType, ctx: DecodingContext):
}
const struct: any = {}
for (const field of fields) {
+ const fieldValue = value[field.name]
+ if (
+ field.type.isExtension &&
+ !ctx.strictExtensions &&
+ (fieldValue === undefined || (fieldValue === null && !field.type.isOptional))
+ ) {
+ continue
+ }
ctx.codingPath.push({field: field.name, type: field.type})
- struct[field.name] = decodeObject(value[field.name], field.type, ctx)
+ struct[field.name] = decodeObject(fieldValue, field.type, ctx)
ctx.codingPath.pop()
}
if (abiType) {
diff --git a/packages/antelope/test/chain.ts b/packages/antelope/test/chain.ts
index de18ad22..4a239e4a 100644
--- a/packages/antelope/test/chain.ts
+++ b/packages/antelope/test/chain.ts
@@ -540,6 +540,11 @@ suite('chain', function () {
true
)
+ const nullKey = PublicKey.from('EOS1111111111111111111111111111111114T1Anm')
+ assert.equal(nullKey.data.length, 33)
+ assert.equal(nullKey.toLegacyString(), 'EOS1111111111111111111111111111111114T1Anm')
+ assert.equal(nullKey.toString(), 'PUB_K1_11111111111111111111111111111111149Mr2R')
+
const sig = Signature.from(
'SIG_K1_JyMXe1HU42qN2aM7GPUf5XrAcAjWPbRoojzfsKq9Rgto3dGsRcCZ4UaPsAcFPS2faGQMpRoSTRX8WQQUDEA5TfWHj8sr6q'
)
diff --git a/packages/antelope/test/serializer.ts b/packages/antelope/test/serializer.ts
index 7f1c0302..81a07202 100644
--- a/packages/antelope/test/serializer.ts
+++ b/packages/antelope/test/serializer.ts
@@ -1222,6 +1222,108 @@ suite('serializer', function () {
assert.notProperty(Serializer.objectify(res1), 'proposal_hash')
})
+ test('absent binary extensions are omitted', function () {
+ const abi = ABI.from({
+ structs: [
+ {
+ name: 'permission_level',
+ base: '',
+ fields: [
+ {name: 'actor', type: 'name'},
+ {name: 'permission', type: 'name'},
+ ],
+ },
+ {
+ name: 'approve',
+ base: '',
+ fields: [
+ {name: 'proposer', type: 'name'},
+ {name: 'proposal_name', type: 'name'},
+ {name: 'level', type: 'permission_level'},
+ {name: 'proposal_hash', type: 'checksum256$'},
+ ],
+ },
+ {
+ name: 'optionals',
+ base: '',
+ fields: [
+ {name: 'name', type: 'name'},
+ {name: 'maybe', type: 'checksum256?'},
+ {name: 'maybe_ext', type: 'bool?$'},
+ ],
+ },
+ ],
+ })
+ const action = {
+ proposer: 'foo',
+ proposal_name: 'bar',
+ level: {actor: 'baz', permission: 'active'},
+ }
+ const data = Serializer.encode({object: action, abi, type: 'approve'})
+ const fromBinary = Serializer.decode({data, abi, type: 'approve'})
+ assert.notProperty(fromBinary, 'proposal_hash')
+ assert.deepEqual(Serializer.objectify(fromBinary), action)
+ assert.equal(
+ Serializer.encode({object: fromBinary, abi, type: 'approve'}).hexString,
+ data.hexString
+ )
+
+ const fromObject = Serializer.decode({object: action, abi, type: 'approve'})
+ assert.notProperty(fromObject, 'proposal_hash')
+ const fromNull = Serializer.decode({
+ object: {...action, proposal_hash: null},
+ abi,
+ type: 'approve',
+ })
+ assert.notProperty(fromNull, 'proposal_hash')
+
+ const optionals = Serializer.decode({
+ data: Serializer.encode({
+ object: {name: 'foo', maybe: null, maybe_ext: null},
+ abi,
+ type: 'optionals',
+ }),
+ abi,
+ type: 'optionals',
+ }) as any
+ assert.strictEqual(optionals.maybe, null)
+ assert.strictEqual(optionals.maybe_ext, null)
+ const optionalsAbsent = Serializer.decode({
+ data: '0000000000000000' + '00',
+ abi,
+ type: 'optionals',
+ })
+ assert.strictEqual((optionalsAbsent as any).maybe, null)
+ assert.notProperty(optionalsAbsent, 'maybe_ext')
+ })
+
+ test('absent binary extensions are omitted from structs', function () {
+ @Struct.type('approve_ext')
+ class ApproveExt extends Struct {
+ @Struct.field(Name) declare proposer: Name
+ @Struct.field('checksum256$') declare proposal_hash?: Checksum256
+ @Struct.field(Checksum256, {extension: true}) declare other_hash?: Checksum256
+ }
+ const data = Serializer.encode({object: ApproveExt.from({proposer: 'foo'})})
+ const decoded = Serializer.decode({data, type: ApproveExt})
+ assert.notProperty(decoded, 'proposal_hash')
+ assert.notProperty(decoded, 'other_hash')
+ assert.deepEqual(Serializer.objectify(decoded), {proposer: 'foo'})
+ assert.equal(Serializer.encode({object: decoded}).hexString, data.hexString)
+
+ const hash = '0000000000000000000000000000000000000000000000000000000000000001'
+ const full = ApproveExt.from({proposer: 'foo', proposal_hash: hash, other_hash: hash})
+ const fullDecoded = Serializer.decode({
+ data: Serializer.encode({object: full}),
+ type: ApproveExt,
+ })
+ assert.deepEqual(Serializer.objectify(fullDecoded), {
+ proposer: 'foo',
+ proposal_hash: hash,
+ other_hash: hash,
+ })
+ })
+
test('action_results', function () {
const raw = {
____comment: 'This file was generated with eosio-abigen. DO NOT EDIT ',
diff --git a/packages/atomicassets/README.md b/packages/atomicassets/README.md
index 6dcdaafc..ed67b30b 100644
--- a/packages/atomicassets/README.md
+++ b/packages/atomicassets/README.md
@@ -8,8 +8,79 @@ AtomicAssets library for Wharf
yarn add @wharfkit/atomicassets
```
+## AtomicAssets v2 and AtomicMarket v2
+
+The v2 contracts add fields that the API returns only from a v2 indexer. Every one of them is
+declared optional, so a response from a v1 indexer decodes unchanged and the field reads back
+empty.
+
+| Field | Returned on | Meaning |
+| --- | --- | --- |
+| `current_collection_fee` | sales, auctions, buyoffers, template buyoffers | The collection's `market_fee` as of the last indexed block. The nested `collection.market_fee` is the listing-time snapshot, and the two differ while the listing is open. |
+| `types` | schemas | The media type descriptors authored through `setschematyp`, exactly as stored. |
+| `mutable_data`, `data` | templates | The data written by `settempldata`, and the immutable and mutable data merged. |
+| `deleted_at_block`, `deleted_at_time` | templates | Set once `deltemplate` removes the template. |
+| `new_author_name`, `new_author_date` | collections | The pending author succession written by `createauswap`, and the date it may be accepted. |
+
+A schema also reports `mediatype` and `info` inside `format[]`, but the API merges the authored
+descriptors with a name and type heuristic there, so a reader cannot tell a stored value from a
+derived one. `types` reports the authored ones alone. This matters to a client that calls
+`setschematyp`, which replaces the whole array: resubmitting a `format[]` value writes the
+heuristic's guess to chain. An empty `types` array means the schema has no descriptors, and an
+absent one means the response does not report them, which is what a schema nested inside an asset
+or template answers.
+
+### Royalties
+
+The AtomicMarket v2 royalty tables and the settled payout ledger are read through the v1 market
+client. The example needs an indexer of an AtomicMarket v2 chain, such as the WAX testnet one
+shown below:
+
+```ts
+import {APIClient, FetchProvider} from '@wharfkit/antelope'
+import {AtomicAssetsAPIClient} from '@wharfkit/atomicassets'
+
+const api = new AtomicAssetsAPIClient(
+ new APIClient({provider: new FetchProvider('https://test.wax.api.atomicassets.io')})
+)
+const market = api.atomicmarket.v1
+
+// The royaltyconf, royaltytemp, and royaltyattr rows of one collection.
+const config = await market.get_royalty_config('mycollection')
+const templateRules = await market.get_royalty_template_rules('mycollection')
+const attributeRules = await market.get_royalty_attribute_rules('mycollection')
+
+// The payouts an account has been paid, the total number of them, and the
+// per-token totals.
+const payouts = await market.get_royalty_payouts({recipient: ['myaccount'], limit: 100})
+const payoutCount = await market.get_royalty_payouts_count({recipient: ['myaccount']})
+const totals = await market.get_royalty_account('myaccount')
+```
+
+A payout carries `amount` in the token's smallest unit, `category` naming which rule paid
+(`founders`, `template`, `attribute`, or `dust`), and a nullable `listing_id`, `asset_id`,
+`template_id`, and `rule_id`. `get_royalty_account` answers one row per token symbol.
+
+A collection with no royalty configuration answers HTTP 416, which reaches the caller as an
+`APIError` with `error.response.status === 416`. On an AtomicMarket v1 chain every collection
+answers that way, and the payout routes answer an empty array. An indexer without the royalty
+routes answers 404 on all six methods.
+
## Running Tests
```
make test
```
+
+The suite never contacts the network. `@wharfkit/mock-data` replays recorded responses from
+`test/data`, where each filename is a hash of the full request URL and its parameters, so
+responses from two hosts never collide. To re-record one fixture, name its test:
+
+```
+MOCK=overwrite make test grep='get_royalty_payouts'
+```
+
+`MOCK=overwrite make test` without a `grep=` re-records every fixture in `test/data` against the
+live hosts. The royalty fixtures come from `V2_BASE_URL` in `test/config.ts`, an AtomicMarket v2
+indexer, because the default host indexes an AtomicMarket v1 chain where those routes answer 416
+or an empty array.
diff --git a/packages/atomicassets/package.json b/packages/atomicassets/package.json
index d493bba3..05878b98 100644
--- a/packages/atomicassets/package.json
+++ b/packages/atomicassets/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/atomicassets",
"description": "AtomicAsset library for Wharf",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/atomicassets/src/endpoints/assets/v1.ts b/packages/atomicassets/src/endpoints/assets/v1.ts
index c89b9963..3524433f 100644
--- a/packages/atomicassets/src/endpoints/assets/v1.ts
+++ b/packages/atomicassets/src/endpoints/assets/v1.ts
@@ -4,7 +4,7 @@ import type {ActionNames as ActionType} from '../../contracts/atomicassets'
import {CountResponseStruct} from '../../types'
import * as Assets from './types'
import type {OfferState} from '../../types'
-import {buildBodyParams} from '../utils'
+import {buildBodyParams, pathSegment} from '../utils'
export interface GetAssetsOptions {
collection_name?: NameType[]
@@ -233,7 +233,7 @@ export class AssetsV1APIClient {
async get_asset(asset_id: UInt64Type) {
return this.client.call({
- path: `/atomicassets/v1/assets/${asset_id}`,
+ path: `/atomicassets/v1/assets/${pathSegment(asset_id)}`,
method: 'GET',
responseType: Assets.GetAssetResponse,
})
@@ -241,7 +241,7 @@ export class AssetsV1APIClient {
async get_asset_stats(asset_id: UInt64Type) {
return this.client.call({
- path: `/atomicassets/v1/assets/${asset_id}/stats`,
+ path: `/atomicassets/v1/assets/${pathSegment(asset_id)}/stats`,
method: 'GET',
responseType: Assets.GetAssetStatsResponse,
})
@@ -260,7 +260,7 @@ export class AssetsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicassets/v1/assets/${asset_id}/logs`,
+ path: `/atomicassets/v1/assets/${pathSegment(asset_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -294,7 +294,7 @@ export class AssetsV1APIClient {
async get_collection(collection_name: NameType) {
return this.client.call({
- path: `/atomicassets/v1/collections/${collection_name}`,
+ path: `/atomicassets/v1/collections/${pathSegment(collection_name)}`,
method: 'GET',
responseType: Assets.GetCollectionResponse,
})
@@ -302,7 +302,7 @@ export class AssetsV1APIClient {
async get_collection_stats(collection_name: NameType) {
return this.client.call({
- path: `/atomicassets/v1/collections/${collection_name}/stats`,
+ path: `/atomicassets/v1/collections/${pathSegment(collection_name)}/stats`,
method: 'GET',
responseType: Assets.GetCollectionStatsResponse,
})
@@ -310,7 +310,7 @@ export class AssetsV1APIClient {
async get_collection_schemas(collection_name: NameType) {
return this.client.call({
- path: `/atomicassets/v1/collections/${collection_name}/schemas`,
+ path: `/atomicassets/v1/collections/${pathSegment(collection_name)}/schemas`,
method: 'GET',
responseType: Assets.GetCollectionSchemasResponse,
})
@@ -329,7 +329,7 @@ export class AssetsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicassets/v1/collections/${collection_name}/logs`,
+ path: `/atomicassets/v1/collections/${pathSegment(collection_name)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -363,7 +363,9 @@ export class AssetsV1APIClient {
async get_schema(collection_name: NameType, schema_name: NameType) {
return this.client.call({
- path: `/atomicassets/v1/schemas/${collection_name}/${schema_name}`,
+ path: `/atomicassets/v1/schemas/${pathSegment(collection_name)}/${pathSegment(
+ schema_name
+ )}`,
method: 'GET',
responseType: Assets.GetSchemaResponse,
})
@@ -371,7 +373,9 @@ export class AssetsV1APIClient {
async get_schema_stats(collection_name: NameType, schema_name: NameType) {
return this.client.call({
- path: `/atomicassets/v1/schemas/${collection_name}/${schema_name}/stats`,
+ path: `/atomicassets/v1/schemas/${pathSegment(collection_name)}/${pathSegment(
+ schema_name
+ )}/stats`,
method: 'GET',
responseType: Assets.GetSchemaStatsResponse,
})
@@ -391,7 +395,9 @@ export class AssetsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicassets/v1/schemas/${collection_name}/${schema_name}/logs`,
+ path: `/atomicassets/v1/schemas/${pathSegment(collection_name)}/${pathSegment(
+ schema_name
+ )}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -428,7 +434,9 @@ export class AssetsV1APIClient {
async get_template(collection_name: NameType, template_id: Int32Type) {
return this.client.call({
- path: `/atomicassets/v1/templates/${collection_name}/${template_id}`,
+ path: `/atomicassets/v1/templates/${pathSegment(collection_name)}/${pathSegment(
+ template_id
+ )}`,
method: 'GET',
responseType: Assets.GetTemplateResponse,
})
@@ -442,8 +450,10 @@ export class AssetsV1APIClient {
) {
const path =
typeof template_id === 'undefined'
- ? `/atomicassets/v1/templates/${collection_name_or_template_id}/stats`
- : `/atomicassets/v1/templates/${collection_name_or_template_id}/${template_id}/stats`
+ ? `/atomicassets/v1/templates/${pathSegment(collection_name_or_template_id)}/stats`
+ : `/atomicassets/v1/templates/${pathSegment(
+ collection_name_or_template_id
+ )}/${pathSegment(template_id)}/stats`
return this.client.call({
path,
@@ -466,7 +476,9 @@ export class AssetsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicassets/v1/templates/${collection_name}/${template_id}/logs`,
+ path: `/atomicassets/v1/templates/${pathSegment(collection_name)}/${pathSegment(
+ template_id
+ )}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -500,7 +512,7 @@ export class AssetsV1APIClient {
async get_offer(offer_id: UInt64Type) {
return this.client.call({
- path: `/atomicassets/v1/offers/${offer_id}`,
+ path: `/atomicassets/v1/offers/${pathSegment(offer_id)}`,
method: 'GET',
responseType: Assets.GetOfferResponse,
})
@@ -519,7 +531,7 @@ export class AssetsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicassets/v1/offers/${offer_id}/logs`,
+ path: `/atomicassets/v1/offers/${pathSegment(offer_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -586,7 +598,7 @@ export class AssetsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicassets/v1/accounts/${account}`,
+ path: `/atomicassets/v1/accounts/${pathSegment(account)}`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -596,7 +608,9 @@ export class AssetsV1APIClient {
async get_account_template_schema_count(account: NameType, collection_name: NameType) {
return this.client.call({
- path: `/atomicassets/v1/accounts/${account}/${collection_name}`,
+ path: `/atomicassets/v1/accounts/${pathSegment(account)}/${pathSegment(
+ collection_name
+ )}`,
method: 'GET',
responseType: Assets.GetAccountTemplateSchemaCountResponse,
})
@@ -625,7 +639,7 @@ export class AssetsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicassets/v1/burns/${account}`,
+ path: `/atomicassets/v1/burns/${pathSegment(account)}`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
diff --git a/packages/atomicassets/src/endpoints/market/types.ts b/packages/atomicassets/src/endpoints/market/types.ts
index 17a3fabf..b8e0f73d 100644
--- a/packages/atomicassets/src/endpoints/market/types.ts
+++ b/packages/atomicassets/src/endpoints/market/types.ts
@@ -14,6 +14,11 @@ import {
Marketplace,
OfferObject,
ResponseStruct,
+ RoyaltyAccountTotal,
+ RoyaltyAttributeRule,
+ RoyaltyConfig,
+ RoyaltyPayout,
+ RoyaltyTemplateRule,
SaleObject,
SalePrice,
SalePriceDay,
@@ -103,6 +108,32 @@ export class MarketAccount extends Struct {
@Struct.field(AccountStat) declare result: AccountStat
}
+@Struct.type('marketplace_stat')
+export class MarketplaceStat extends Struct {
+ @Struct.field(Name) declare market_contract: Name
+ /**
+ * Null when no marketplace is stored, and the empty string when the
+ * stored value is the default marketplace name. Both mean the same
+ * thing to a client.
+ */
+ @Struct.field('string', {optional: true}) declare marketplace_name: string
+ @Struct.field(UInt64) declare sellers: UInt64
+ @Struct.field(UInt64) declare buyers: UInt64
+ /**
+ * The field is declared optional defensively because the outer sort
+ * carries NULLS LAST. The aggregate answers `0`, not null, for a side
+ * with no rows.
+ */
+ @Struct.field(UInt64, {optional: true}) declare maker_volume: UInt64
+ @Struct.field(UInt64, {optional: true}) declare taker_volume: UInt64
+}
+
+@Struct.type('market_marketplaces')
+export class MarketMarketplaces extends Struct {
+ @Struct.field(Token) declare symbol: Token
+ @Struct.field(MarketplaceStat, {array: true}) declare results: MarketplaceStat[]
+}
+
@Struct.type('schema_stat_v1')
export class SchemaStatV1 extends Struct {
@Struct.field(Name) declare contract: Name
@@ -303,3 +334,33 @@ export class GetStatsGraphResponse extends ResponseStruct {
export class GetStatsSalesResponse extends ResponseStruct {
@Struct.field(MarketSale) declare data: MarketSale
}
+
+@Struct.type('get_stats_markets_resp')
+export class GetStatsMarketsResponse extends ResponseStruct {
+ @Struct.field(MarketMarketplaces) declare data: MarketMarketplaces
+}
+
+@Struct.type('get_royalty_config_resp')
+export class GetRoyaltyConfigResponse extends ResponseStruct {
+ @Struct.field(RoyaltyConfig) declare data: RoyaltyConfig
+}
+
+@Struct.type('get_royalty_template_rules_resp')
+export class GetRoyaltyTemplateRulesResponse extends ResponseStruct {
+ @Struct.field(RoyaltyTemplateRule, {array: true}) declare data: RoyaltyTemplateRule[]
+}
+
+@Struct.type('get_royalty_attribute_rules_resp')
+export class GetRoyaltyAttributeRulesResponse extends ResponseStruct {
+ @Struct.field(RoyaltyAttributeRule, {array: true}) declare data: RoyaltyAttributeRule[]
+}
+
+@Struct.type('get_royalty_payouts_resp')
+export class GetRoyaltyPayoutsResponse extends ResponseStruct {
+ @Struct.field(RoyaltyPayout, {array: true}) declare data: RoyaltyPayout[]
+}
+
+@Struct.type('get_royalty_account_resp')
+export class GetRoyaltyAccountResponse extends ResponseStruct {
+ @Struct.field(RoyaltyAccountTotal, {array: true}) declare data: RoyaltyAccountTotal[]
+}
diff --git a/packages/atomicassets/src/endpoints/market/v1.ts b/packages/atomicassets/src/endpoints/market/v1.ts
index 200f5c17..740fd0ce 100644
--- a/packages/atomicassets/src/endpoints/market/v1.ts
+++ b/packages/atomicassets/src/endpoints/market/v1.ts
@@ -5,7 +5,7 @@ import {CountResponseStruct} from '../../types'
import * as Market from './types'
import type {ActionNames as SActionType} from '../../contracts/atomicassets'
import type {ActionNames as MActionType} from '../../contracts/atomicmarket'
-import {buildBodyParams} from '../utils'
+import {buildBodyParams, pathSegment} from '../utils'
export interface GetAssetsOptions {
collection_name?: NameType[]
@@ -201,6 +201,26 @@ export interface GetTemplateBuyoffersOptions {
sort?: 'created' | 'updated' | 'ending' | 'buyoffer_id' | 'price' | 'template_mint' | 'name'
}
+export interface GetRoyaltyPayoutsOptions {
+ recipient?: NameType[]
+ collection_name?: NameType[]
+ asset_id?: UInt64Type[]
+ symbol?: string
+ listing_type?: 'unresolved' | 'sale' | 'auction' | 'buyoffer' | 'template_buyoffer'
+ listing_id?: UInt64Type
+ category?: Array<'founders' | 'template' | 'attribute' | 'dust'>
+ ids?: UInt64Type[]
+ // lower_bound and upper_bound range over log_global_sequence.
+ lower_bound?: string
+ upper_bound?: string
+ before?: number
+ after?: number
+ page?: number
+ limit?: number
+ order?: 'asc' | 'desc'
+ sort?: 'created' | 'amount'
+}
+
export class MarketV1APIClient {
constructor(private client: APIClient) {}
@@ -230,7 +250,7 @@ export class MarketV1APIClient {
async get_asset(asset_id: UInt64Type) {
return this.client.call({
- path: `/atomicmarket/v1/assets/${asset_id}`,
+ path: `/atomicmarket/v1/assets/${pathSegment(asset_id)}`,
method: 'GET',
responseType: Market.GetAssetResponse,
})
@@ -238,7 +258,7 @@ export class MarketV1APIClient {
async get_asset_stats(asset_id: UInt64Type) {
return this.client.call({
- path: `/atomicmarket/v1/assets/${asset_id}/stats`,
+ path: `/atomicmarket/v1/assets/${pathSegment(asset_id)}/stats`,
method: 'GET',
responseType: Market.GetAssetStatsResponse,
})
@@ -257,7 +277,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/assets/${asset_id}/logs`,
+ path: `/atomicmarket/v1/assets/${pathSegment(asset_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -277,7 +297,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/assets/${asset_id}/sales`,
+ path: `/atomicmarket/v1/assets/${pathSegment(asset_id)}/sales`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -331,7 +351,7 @@ export class MarketV1APIClient {
async get_offer(offer_id: UInt64Type) {
return this.client.call({
- path: `/atomicmarket/v1/offers/${offer_id}`,
+ path: `/atomicmarket/v1/offers/${pathSegment(offer_id)}`,
method: 'GET',
responseType: Market.GetOfferResponse,
})
@@ -350,7 +370,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/offers/${offer_id}/logs`,
+ path: `/atomicmarket/v1/offers/${pathSegment(offer_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -395,7 +415,7 @@ export class MarketV1APIClient {
async get_sale(sale_id: UInt64Type) {
return this.client.call({
- path: `/atomicmarket/v1/sales/${sale_id}`,
+ path: `/atomicmarket/v1/sales/${pathSegment(sale_id)}`,
method: 'GET',
responseType: Market.GetSaleResponse,
})
@@ -414,7 +434,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/sales/${sale_id}/logs`,
+ path: `/atomicmarket/v1/sales/${pathSegment(sale_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -490,7 +510,7 @@ export class MarketV1APIClient {
async get_auction(auction_id: UInt64Type) {
return this.client.call({
- path: `/atomicmarket/v1/auctions/${auction_id}`,
+ path: `/atomicmarket/v1/auctions/${pathSegment(auction_id)}`,
method: 'GET',
responseType: Market.GetAuctionResponse,
})
@@ -509,7 +529,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/auctions/${auction_id}/logs`,
+ path: `/atomicmarket/v1/auctions/${pathSegment(auction_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -546,7 +566,7 @@ export class MarketV1APIClient {
async get_buyoffer(buyoffer_id: UInt64Type) {
return this.client.call({
- path: `/atomicmarket/v1/buyoffers/${buyoffer_id}`,
+ path: `/atomicmarket/v1/buyoffers/${pathSegment(buyoffer_id)}`,
method: 'GET',
responseType: Market.GetBuyofferResponse,
})
@@ -565,7 +585,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/buyoffers/${buyoffer_id}/logs`,
+ path: `/atomicmarket/v1/buyoffers/${pathSegment(buyoffer_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -605,7 +625,7 @@ export class MarketV1APIClient {
async get_template_buyoffer(buyoffer_id: UInt64Type) {
return this.client.call({
- path: `/atomicmarket/v1/template_buyoffers/${buyoffer_id}`,
+ path: `/atomicmarket/v1/template_buyoffers/${pathSegment(buyoffer_id)}`,
method: 'GET',
responseType: Market.GetTemplateBuyofferResponse,
})
@@ -624,7 +644,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/template_buyoffers/${buyoffer_id}/logs`,
+ path: `/atomicmarket/v1/template_buyoffers/${pathSegment(buyoffer_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -642,7 +662,7 @@ export class MarketV1APIClient {
async get_marketplace(marketplace_name: string) {
return this.client.call({
- path: `/atomicmarket/v1/marketplaces/${marketplace_name}`,
+ path: `/atomicmarket/v1/marketplaces/${pathSegment(marketplace_name)}`,
method: 'GET',
responseType: Market.GetMarketplaceResponse,
})
@@ -769,7 +789,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/prices/inventory/${account}`,
+ path: `/atomicmarket/v1/prices/inventory/${pathSegment(account)}`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -813,7 +833,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/stats/collections/${collection_name}`,
+ path: `/atomicmarket/v1/stats/collections/${pathSegment(collection_name)}`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -855,7 +875,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/stats/accounts/${account}`,
+ path: `/atomicmarket/v1/stats/accounts/${pathSegment(account)}`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -878,7 +898,7 @@ export class MarketV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v1/stats/schemas/${collection_name}`,
+ path: `/atomicmarket/v1/stats/schemas/${pathSegment(collection_name)}`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
@@ -946,6 +966,30 @@ export class MarketV1APIClient {
})
}
+ /**
+ * Seller, buyer, and volume totals per marketplace for one token symbol.
+ *
+ * Bound the query with `collection_whitelist` or an `after` window. On a
+ * mainnet-sized indexer the unbounded aggregate exceeds the server timeout.
+ */
+ async get_stats_markets(options: {
+ symbol: string
+ collection_blacklist?: NameType[]
+ collection_whitelist?: NameType[]
+ before?: number
+ after?: number
+ }) {
+ const bodyParams = buildBodyParams(options)
+
+ return this.client.call({
+ path: `/atomicmarket/v1/stats/markets`,
+ method: 'POST',
+ params: bodyParams,
+ headers: {'Content-Type': 'application/json'},
+ responseType: Market.GetStatsMarketsResponse,
+ })
+ }
+
async get_config() {
return this.client.call({
path: '/atomicmarket/v1/config',
@@ -953,4 +997,112 @@ export class MarketV1APIClient {
responseType: Market.GetConfigResponse,
})
}
+
+ /**
+ * The royalty configuration of a collection, mirrored from `royaltyconf`.
+ *
+ * A collection with no configuration answers HTTP 416, which reaches the
+ * caller as an `APIError` with `error.response.status === 416`. That is the
+ * "not configured" answer rather than a failure, and every collection on an
+ * AtomicMarket v1 chain answers that way. An indexer that predates the
+ * royalty routes answers 404.
+ */
+ async get_royalty_config(collection_name: NameType) {
+ return this.client.call({
+ path: `/atomicmarket/v1/royalties/${pathSegment(collection_name)}`,
+ method: 'GET',
+ responseType: Market.GetRoyaltyConfigResponse,
+ })
+ }
+
+ async get_royalty_template_rules(
+ collection_name: NameType,
+ options?: {
+ template_id?: Int32Type[]
+ page?: number
+ limit?: number
+ }
+ ) {
+ const bodyParams = buildBodyParams(options)
+
+ return this.client.call({
+ path: `/atomicmarket/v1/royalties/${pathSegment(collection_name)}/templates`,
+ method: 'POST',
+ params: bodyParams,
+ headers: {'Content-Type': 'application/json'},
+ responseType: Market.GetRoyaltyTemplateRulesResponse,
+ })
+ }
+
+ async get_royalty_attribute_rules(
+ collection_name: NameType,
+ options?: {
+ source?: number
+ field?: string
+ page?: number
+ limit?: number
+ }
+ ) {
+ const bodyParams = buildBodyParams(options)
+
+ return this.client.call({
+ path: `/atomicmarket/v1/royalties/${pathSegment(collection_name)}/attributes`,
+ method: 'POST',
+ params: bodyParams,
+ headers: {'Content-Type': 'application/json'},
+ responseType: Market.GetRoyaltyAttributeRulesResponse,
+ })
+ }
+
+ /** The settled payout ledger. Empty on an AtomicMarket v1 chain. */
+ async get_royalty_payouts(
+ options?: GetRoyaltyPayoutsOptions,
+ extra_options?: {[key: string]: string}
+ ) {
+ const bodyParams = buildBodyParams(options, extra_options)
+
+ return this.client.call({
+ path: `/atomicmarket/v1/royalties/payouts`,
+ method: 'POST',
+ params: bodyParams,
+ headers: {'Content-Type': 'application/json'},
+ responseType: Market.GetRoyaltyPayoutsResponse,
+ })
+ }
+
+ async get_royalty_payouts_count(
+ options?: GetRoyaltyPayoutsOptions,
+ extra_options?: {[key: string]: string}
+ ) {
+ const bodyParams = buildBodyParams(options, extra_options)
+
+ return this.client.call({
+ path: `/atomicmarket/v1/royalties/payouts/_count`,
+ method: 'POST',
+ params: bodyParams,
+ headers: {'Content-Type': 'application/json'},
+ responseType: CountResponseStruct,
+ })
+ }
+
+ /** One row per token symbol the account has been paid in. */
+ async get_royalty_account(
+ account: NameType,
+ options?: {
+ collection_name?: NameType[]
+ symbol?: string
+ before?: number
+ after?: number
+ }
+ ) {
+ const bodyParams = buildBodyParams(options)
+
+ return this.client.call({
+ path: `/atomicmarket/v1/royalties/accounts/${pathSegment(account)}`,
+ method: 'POST',
+ params: bodyParams,
+ headers: {'Content-Type': 'application/json'},
+ responseType: Market.GetRoyaltyAccountResponse,
+ })
+ }
}
diff --git a/packages/atomicassets/src/endpoints/market/v2.ts b/packages/atomicassets/src/endpoints/market/v2.ts
index eabadefa..49c3df25 100644
--- a/packages/atomicassets/src/endpoints/market/v2.ts
+++ b/packages/atomicassets/src/endpoints/market/v2.ts
@@ -3,7 +3,7 @@ import type {Float64Type, Int32Type, NameType, UInt64Type} from '@wharfkit/antel
import type {SaleState} from '../../types'
import {CountResponseStruct} from '../../types'
import * as Market from './types'
-import {buildBodyParams} from '../utils'
+import {buildBodyParams, pathSegment} from '../utils'
export interface GetSalesOptions {
state?: SaleState[]
@@ -96,7 +96,7 @@ export class MarketV2APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomicmarket/v2/stats/schemas/${collection_name}`,
+ path: `/atomicmarket/v2/stats/schemas/${pathSegment(collection_name)}`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
diff --git a/packages/atomicassets/src/endpoints/tools/v1.ts b/packages/atomicassets/src/endpoints/tools/v1.ts
index a36c1f2e..412d81f3 100644
--- a/packages/atomicassets/src/endpoints/tools/v1.ts
+++ b/packages/atomicassets/src/endpoints/tools/v1.ts
@@ -4,7 +4,7 @@ import type {LinkState} from '../../types'
import {CountResponseStruct} from '../../types'
import * as Tools from './types'
import type {ActionNames as ActionType} from '../../contracts/atomictoolsx'
-import {buildBodyParams} from '../utils'
+import {buildBodyParams, pathSegment} from '../utils'
export interface GetLinksOptions {
creator?: NameType[]
@@ -54,7 +54,7 @@ export class ToolsV1APIClient {
async get_link(link_id: UInt64Type) {
return this.client.call({
- path: `/atomictools/v1/links/${link_id}`,
+ path: `/atomictools/v1/links/${pathSegment(link_id)}`,
method: 'GET',
responseType: Tools.GetLinkResponse,
})
@@ -73,7 +73,7 @@ export class ToolsV1APIClient {
const bodyParams = buildBodyParams(options)
return this.client.call({
- path: `/atomictools/v1/links/${link_id}/logs`,
+ path: `/atomictools/v1/links/${pathSegment(link_id)}/logs`,
method: 'POST',
params: bodyParams,
headers: {'Content-Type': 'application/json'},
diff --git a/packages/atomicassets/src/endpoints/types.ts b/packages/atomicassets/src/endpoints/types.ts
index 660692e6..c85631c8 100644
--- a/packages/atomicassets/src/endpoints/types.ts
+++ b/packages/atomicassets/src/endpoints/types.ts
@@ -136,17 +136,38 @@ export class CollectionObject extends Struct {
* A schema format field as returned by the API.
*
* This is intentionally not the contract's `FORMAT` struct: `createschema` takes
- * only `{name, type}`, while the API additionally reports the `mediatype` and
- * `info` recorded by the AtomicAssets v2 `setschematyp` action. Widening the
- * contract struct would let those fields leak into serialized action data.
+ * only `{name, type}`, while the API additionally reports a `mediatype` and an
+ * `info` descriptor per field. Widening the contract struct would let those
+ * fields leak into serialized action data.
*/
@Struct.type('schema_format_field')
export class SchemaFormatField extends Struct {
@Struct.field('string') declare name: string
@Struct.field('string') declare type: string
- /** Media type recorded by `setschematyp`. AtomicAssets v2 only. */
+ /**
+ * Media type for the field. The API merges the descriptors authored through
+ * `setschematyp` with a name and type heuristic, so a value here can be
+ * derived rather than stored. `SchemaObject.types` reports the authored
+ * ones. AtomicAssets v2 only.
+ */
+ @Struct.field('string', {optional: true}) declare mediatype: string
+ /**
+ * Free-form descriptor for the field, merged with the same heuristic as
+ * `mediatype`. `SchemaObject.types` reports the authored ones.
+ * AtomicAssets v2 only.
+ */
+ @Struct.field('string', {optional: true}) declare info: string
+}
+
+/**
+ * A media-type descriptor authored through `setschematyp`, exactly as stored on
+ * chain. It carries no serialization `type`, which is what separates it from
+ * `SchemaFormatField`.
+ */
+@Struct.type('schema_format_type')
+export class SchemaFormatType extends Struct {
+ @Struct.field('string') declare name: string
@Struct.field('string', {optional: true}) declare mediatype: string
- /** Free-form descriptor recorded by `setschematyp`. AtomicAssets v2 only. */
@Struct.field('string', {optional: true}) declare info: string
}
@@ -156,6 +177,15 @@ export class SchemaObject extends Struct {
@Struct.field(UInt64, {optional: true}) declare assets: UInt64
@Struct.field(SchemaFormatField, {array: true})
declare format: SchemaFormatField[]
+ /**
+ * The descriptors authored through `setschematyp`, unmerged. Returned by
+ * `/schemas` and `/schemas/{collection_name}/{schema_name}` alone, so a
+ * schema nested in an asset, template, or account response omits it. An
+ * empty array means the schema has none, and an absent one means the
+ * response does not report them. AtomicAssets v2 only.
+ */
+ @Struct.field(SchemaFormatType, {array: true, optional: true})
+ declare types: SchemaFormatType[]
@Struct.field(Name, {optional: true}) declare contract: Name
@Struct.field(Name, {optional: true}) declare collection_name: Name
@Struct.field(CollectionObject, {optional: true}) declare collection: CollectionObject
@@ -339,7 +369,11 @@ export class SaleObject extends Struct {
@Struct.field('string') declare updated_at_time: string
@Struct.field(UInt64) declare created_at_block: UInt64
@Struct.field('string') declare created_at_time: string
- /** Collection fee resolved at listing time. AtomicMarket v2 only. */
+ /**
+ * The collection's `market_fee` as of the last indexed block. The nested
+ * `collection.market_fee` is the listing-time snapshot, and the two differ
+ * while the listing is open. AtomicMarket v2 only, undefined on a v1 indexer.
+ */
@Struct.field(Float64, {optional: true}) declare current_collection_fee: Float64
}
@@ -376,6 +410,11 @@ export class AuctionObject extends Struct {
@Struct.field('string') declare created_at_time: string
@Struct.field(UInt64) declare updated_at_block: UInt64
@Struct.field('string') declare updated_at_time: string
+ /**
+ * The collection's live market_fee; see SaleObject.current_collection_fee.
+ * AtomicMarket v2 only.
+ */
+ @Struct.field(Float64, {optional: true}) declare current_collection_fee: Float64
}
@Struct.type('buyoffer_object')
@@ -398,6 +437,11 @@ export class BuyofferObject extends Struct {
@Struct.field(UInt64) declare updated_at_block: UInt64
@Struct.field('string') declare updated_at_time: string
@Struct.field(UInt8) declare state: UInt8
+ /**
+ * The collection's live market_fee; see SaleObject.current_collection_fee.
+ * AtomicMarket v2 only.
+ */
+ @Struct.field(Float64, {optional: true}) declare current_collection_fee: Float64
}
@Struct.type('template_buyoffer_object')
@@ -419,6 +463,11 @@ export class TemplateBuyofferObject extends Struct {
@Struct.field(UInt64) declare updated_at_block: UInt64
@Struct.field('string') declare updated_at_time: string
@Struct.field(UInt8) declare state: UInt8
+ /**
+ * The collection's live market_fee; see SaleObject.current_collection_fee.
+ * AtomicMarket v2 only.
+ */
+ @Struct.field(Float64, {optional: true}) declare current_collection_fee: Float64
}
@Struct.type('marketplace')
@@ -429,6 +478,116 @@ export class Marketplace extends Struct {
@Struct.field('string') declare created_at_time: string
}
+/**
+ * A royalty recipient and its weight as returned by the API.
+ *
+ * This is intentionally not the AtomicMarket contract's `ROYALTYPAIR` struct.
+ * That one serializes action data for `setroyalconf` and the rule actions, this
+ * one only decodes an API row, and keeping them apart is what stops a decoded
+ * row from reaching the wire.
+ */
+@Struct.type('royalty_pair')
+export class RoyaltyPair extends Struct {
+ @Struct.field(Name) declare recipient: Name
+ @Struct.field(UInt32) declare weight: UInt32
+}
+
+/**
+ * The `royaltyconf` row for a collection, mirrored field for field.
+ *
+ * The API answers HTTP 416 for a collection that has no row, and every
+ * collection on an AtomicMarket v1 chain answers that way. AtomicMarket v2 only.
+ */
+@Struct.type('royalty_config')
+export class RoyaltyConfig extends Struct {
+ @Struct.field(Name) declare market_contract: Name
+ @Struct.field(Name) declare collection_name: Name
+ @Struct.field(RoyaltyPair, {array: true}) declare founders: RoyaltyPair[]
+ /** How attribute rules combine: 0 merged, 1 granular. */
+ @Struct.field(UInt8) declare attribute_mode: UInt8
+ @Struct.field(UInt32) declare split_founders: UInt32
+ @Struct.field(UInt32) declare split_templates: UInt32
+ @Struct.field(UInt32) declare split_attributes: UInt32
+ @Struct.field(UInt64) declare updated_at_block: UInt64
+ @Struct.field('string') declare updated_at_time: string
+ @Struct.field(UInt64) declare created_at_block: UInt64
+ @Struct.field('string') declare created_at_time: string
+}
+
+/** A `royaltytemp` row: the per-template recipient override. AtomicMarket v2 only. */
+@Struct.type('royalty_template_rule')
+export class RoyaltyTemplateRule extends Struct {
+ @Struct.field(Name) declare market_contract: Name
+ @Struct.field(Name) declare collection_name: Name
+ @Struct.field(Int32) declare template_id: Int32
+ @Struct.field(RoyaltyPair, {array: true}) declare recipients: RoyaltyPair[]
+ @Struct.field(UInt64) declare updated_at_block: UInt64
+ @Struct.field('string') declare updated_at_time: string
+ @Struct.field(UInt64) declare created_at_block: UInt64
+ @Struct.field('string') declare created_at_time: string
+}
+
+/** A `royaltyattr` row: the attribute-matched recipient override. AtomicMarket v2 only. */
+@Struct.type('royalty_attribute_rule')
+export class RoyaltyAttributeRule extends Struct {
+ @Struct.field(Name) declare market_contract: Name
+ @Struct.field(Name) declare collection_name: Name
+ @Struct.field(UInt64) declare rule_id: UInt64
+ /** Which data source the matched attribute is read from, as the contract stores it. */
+ @Struct.field(UInt8) declare source: UInt8
+ @Struct.field('string') declare field: string
+ /**
+ * The raw `["type", value]` variant tuple the rule matches on. It is kept
+ * untyped so an integer payload stays the string the API sent and nothing
+ * is reparsed. The content is chain-authored by the collection and relayed
+ * by the indexer, so a caller shape-checks it before use.
+ */
+ @Struct.field('any') declare value: [string, unknown]
+ @Struct.field(UInt32) declare weight: UInt32
+ @Struct.field(RoyaltyPair, {array: true}) declare recipients: RoyaltyPair[]
+ /** Hex-encoded sha256 of the matched attribute, as the contract stores it. */
+ @Struct.field('string') declare lookup_hash: string
+ @Struct.field(UInt64) declare updated_at_block: UInt64
+ @Struct.field('string') declare updated_at_time: string
+ @Struct.field(UInt64) declare created_at_block: UInt64
+ @Struct.field('string') declare created_at_time: string
+}
+
+/**
+ * One settled royalty payout, keyed by the log trace global sequence and the
+ * entry's position in the payouts vector. AtomicMarket v2 only.
+ */
+@Struct.type('royalty_payout')
+export class RoyaltyPayout extends Struct {
+ @Struct.field(Name) declare market_contract: Name
+ @Struct.field(UInt64) declare log_global_sequence: UInt64
+ @Struct.field(UInt32) declare payout_index: UInt32
+ /** One of unresolved, sale, auction, buyoffer, template_buyoffer. */
+ @Struct.field('string', {optional: true}) declare listing_type: string
+ @Struct.field(UInt64, {optional: true}) declare listing_id: UInt64
+ /** One of founders, template, attribute, dust. */
+ @Struct.field('string', {optional: true}) declare category: string
+ @Struct.field(Name) declare collection_name: Name
+ @Struct.field(UInt64, {optional: true}) declare asset_id: UInt64
+ @Struct.field(Int32, {optional: true}) declare template_id: Int32
+ @Struct.field(UInt64, {optional: true}) declare rule_id: UInt64
+ @Struct.field(Name) declare recipient: Name
+ @Struct.field(UInt64) declare amount: UInt64
+ @Struct.field('string') declare token_symbol: string
+ @Struct.field(UInt8) declare token_precision: UInt8
+ @Struct.field(Name) declare token_contract: Name
+ /** Hex-encoded id of the transaction the payout was logged in. */
+ @Struct.field('string') declare txid: string
+ @Struct.field(UInt64) declare created_at_block: UInt64
+ @Struct.field('string') declare created_at_time: string
+}
+
+/** An account's settled royalties for one token symbol. AtomicMarket v2 only. */
+@Struct.type('royalty_account_total')
+export class RoyaltyAccountTotal extends TokenAmount {
+ @Struct.field(UInt64) declare payout_count: UInt64
+}
+
@Struct.type('saleprice')
export class SalePrice extends Struct {
@Struct.field(UInt64, {optional: true}) declare sale_id: UInt64
diff --git a/packages/atomicassets/src/endpoints/utils.ts b/packages/atomicassets/src/endpoints/utils.ts
index 48cd95cc..d92111da 100644
--- a/packages/atomicassets/src/endpoints/utils.ts
+++ b/packages/atomicassets/src/endpoints/utils.ts
@@ -41,3 +41,24 @@ export function serializeQueryParams(params?: {[key: string]: any}): {[key: stri
return result
}
+
+/**
+ * Encode a value for use as one path segment.
+ *
+ * An empty value or a dot segment would rewrite the request path, so both are rejected, as is a
+ * missing value, which would otherwise travel as the literal segment 'undefined'. A value that
+ * equals a sibling route literal such as '_count' is a valid segment and is not rejected here.
+ */
+export function pathSegment(value: unknown): string {
+ if (value === null || value === undefined) {
+ throw new Error('Invalid path segment: a value is required')
+ }
+ const segment = String(value)
+ if (segment === '' || segment === '.' || segment === '..') {
+ throw new Error(
+ `Invalid path segment '${segment}': an empty or dot segment rewrites the request path`
+ )
+ }
+
+ return encodeURIComponent(segment)
+}
diff --git a/packages/atomicassets/src/objects/schema.ts b/packages/atomicassets/src/objects/schema.ts
index 67fa38bc..f55a7193 100644
--- a/packages/atomicassets/src/objects/schema.ts
+++ b/packages/atomicassets/src/objects/schema.ts
@@ -33,6 +33,15 @@ export class Schema {
return this.data.format
}
+ /**
+ * The descriptors authored through `setschematyp`. Undefined when the
+ * schema came from a response that does not report them, such as the schema
+ * nested in an asset or template.
+ */
+ get types() {
+ return this.data.types
+ }
+
extendSchema(
authorizedEditor: NameType,
schemaFormat: AtomicAssetsContract.ActionParams.Type.FORMAT[]
diff --git a/packages/atomicassets/test/api.ts b/packages/atomicassets/test/api.ts
index 5c63ea8e..69e528e5 100644
--- a/packages/atomicassets/test/api.ts
+++ b/packages/atomicassets/test/api.ts
@@ -779,6 +779,21 @@ suite('atomicmarket', function () {
assert.isNotEmpty(res.data)
})
+ test('get_stats_markets', async function () {
+ // The aggregate runs over every sale the indexer holds, so an unbounded
+ // call answers 408 on a mainnet-sized host. One collection and an after
+ // window keep it inside the server timeout.
+ const res = await atomicassets.atomicmarket.v1.get_stats_markets({
+ symbol: 'WAX',
+ collection_whitelist: ['alien.worlds'],
+ after: 1750000000000,
+ })
+ assert.instanceOf(res, Types.Market.GetStatsMarketsResponse)
+ assert.equal(res.success, true)
+ assert.isNotEmpty(res.data.symbol)
+ assert.isNotEmpty(res.data.results)
+ })
+
test('get_config', async function () {
const res = await atomicassets.atomicmarket.v1.get_config()
assert.instanceOf(res, Types.Market.GetConfigResponse)
diff --git a/packages/atomicassets/test/config.ts b/packages/atomicassets/test/config.ts
index 0186006c..a539d6ee 100644
--- a/packages/atomicassets/test/config.ts
+++ b/packages/atomicassets/test/config.ts
@@ -3,10 +3,15 @@ export const TEST_CONFIG = {
// Base URL for the AtomicAssets API
BASE_URL: 'https://wax-atomic.alcor.exchange/',
+ // Base URL for an AtomicMarket v2 indexer. The host above indexes WAX
+ // mainnet, an AtomicMarket v1 chain, where every royalty route answers 416
+ // or an empty array, so the royalty fixtures are recorded here instead.
+ V2_BASE_URL: 'https://test.wax.api.atomicassets.io',
+
// Default test timeout and slow thresholds
TIMEOUT: 10 * 1000,
SLOW_THRESHOLD: 300,
} as const
// Export individual values for convenience
-export const {BASE_URL, TIMEOUT, SLOW_THRESHOLD} = TEST_CONFIG
+export const {BASE_URL, V2_BASE_URL, TIMEOUT, SLOW_THRESHOLD} = TEST_CONFIG
diff --git a/packages/atomicassets/test/data/25aa8dfbe47ba945e536c03785dd45e07b6a8297.json b/packages/atomicassets/test/data/25aa8dfbe47ba945e536c03785dd45e07b6a8297.json
new file mode 100644
index 00000000..a880b83a
--- /dev/null
+++ b/packages/atomicassets/test/data/25aa8dfbe47ba945e536c03785dd45e07b6a8297.json
@@ -0,0 +1,35 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/royaltycol11/templates",
+ "params": {
+ "method": "POST",
+ "body": "{}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": [
+ {
+ "market_contract": "atomicmarket",
+ "collection_name": "royaltycol11",
+ "template_id": "662912",
+ "recipients": [
+ {
+ "weight": 1,
+ "recipient": "pe2etestacct"
+ }
+ ],
+ "updated_at_block": "414895353",
+ "updated_at_time": "1783371584000",
+ "created_at_block": "414895353",
+ "created_at_time": "1783371584000"
+ }
+ ],
+ "query_time": 1787002594984
+ },
+ "text": "{\"success\":true,\"data\":[{\"market_contract\":\"atomicmarket\",\"collection_name\":\"royaltycol11\",\"template_id\":\"662912\",\"recipients\":[{\"weight\":1,\"recipient\":\"pe2etestacct\"}],\"updated_at_block\":\"414895353\",\"updated_at_time\":\"1783371584000\",\"created_at_block\":\"414895353\",\"created_at_time\":\"1783371584000\"}],\"query_time\":1787002594984}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/31641a1006fdc5b9661c49ab5c64fc468c74cc3d.json b/packages/atomicassets/test/data/31641a1006fdc5b9661c49ab5c64fc468c74cc3d.json
new file mode 100644
index 00000000..769ec7df
--- /dev/null
+++ b/packages/atomicassets/test/data/31641a1006fdc5b9661c49ab5c64fc468c74cc3d.json
@@ -0,0 +1,60 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/payouts",
+ "params": {
+ "method": "POST",
+ "body": "{\"limit\":\"2\"}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": [
+ {
+ "market_contract": "atomicmarket",
+ "log_global_sequence": "840330124",
+ "payout_index": 0,
+ "listing_type": "sale",
+ "listing_id": "46890",
+ "category": "attribute",
+ "collection_name": "royaltycol11",
+ "asset_id": "1099603751717",
+ "template_id": null,
+ "rule_id": "2",
+ "recipient": "jacktestr125",
+ "amount": "1250000",
+ "token_symbol": "WAX",
+ "token_precision": 8,
+ "token_contract": "eosio.token",
+ "txid": "6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a",
+ "created_at_block": "414927681",
+ "created_at_time": "1783387748000"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "log_global_sequence": "840330123",
+ "payout_index": 0,
+ "listing_type": "sale",
+ "listing_id": "46890",
+ "category": "template",
+ "collection_name": "royaltycol11",
+ "asset_id": "1099603751717",
+ "template_id": "662912",
+ "rule_id": null,
+ "recipient": "pe2etestacct",
+ "amount": "1250000",
+ "token_symbol": "WAX",
+ "token_precision": 8,
+ "token_contract": "eosio.token",
+ "txid": "6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a",
+ "created_at_block": "414927681",
+ "created_at_time": "1783387748000"
+ }
+ ],
+ "query_time": 1787002593620
+ },
+ "text": "{\"success\":true,\"data\":[{\"market_contract\":\"atomicmarket\",\"log_global_sequence\":\"840330124\",\"payout_index\":0,\"listing_type\":\"sale\",\"listing_id\":\"46890\",\"category\":\"attribute\",\"collection_name\":\"royaltycol11\",\"asset_id\":\"1099603751717\",\"template_id\":null,\"rule_id\":\"2\",\"recipient\":\"jacktestr125\",\"amount\":\"1250000\",\"token_symbol\":\"WAX\",\"token_precision\":8,\"token_contract\":\"eosio.token\",\"txid\":\"6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a\",\"created_at_block\":\"414927681\",\"created_at_time\":\"1783387748000\"},{\"market_contract\":\"atomicmarket\",\"log_global_sequence\":\"840330123\",\"payout_index\":0,\"listing_type\":\"sale\",\"listing_id\":\"46890\",\"category\":\"template\",\"collection_name\":\"royaltycol11\",\"asset_id\":\"1099603751717\",\"template_id\":\"662912\",\"rule_id\":null,\"recipient\":\"pe2etestacct\",\"amount\":\"1250000\",\"token_symbol\":\"WAX\",\"token_precision\":8,\"token_contract\":\"eosio.token\",\"txid\":\"6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a\",\"created_at_block\":\"414927681\",\"created_at_time\":\"1783387748000\"}],\"query_time\":1787002593620}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/49c5d8f0ee932826bbf7b7c92c4f4dd3f90d33c5.json b/packages/atomicassets/test/data/49c5d8f0ee932826bbf7b7c92c4f4dd3f90d33c5.json
new file mode 100644
index 00000000..1a621a0b
--- /dev/null
+++ b/packages/atomicassets/test/data/49c5d8f0ee932826bbf7b7c92c4f4dd3f90d33c5.json
@@ -0,0 +1,60 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/payouts",
+ "params": {
+ "method": "POST",
+ "body": "{\"category\":\"attribute\",\"listing_type\":\"sale\",\"sort\":\"amount\",\"order\":\"asc\",\"limit\":\"2\"}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": [
+ {
+ "market_contract": "atomicmarket",
+ "log_global_sequence": "840330124",
+ "payout_index": 0,
+ "listing_type": "sale",
+ "listing_id": "46890",
+ "category": "attribute",
+ "collection_name": "royaltycol11",
+ "asset_id": "1099603751717",
+ "template_id": null,
+ "rule_id": "2",
+ "recipient": "jacktestr125",
+ "amount": "1250000",
+ "token_symbol": "WAX",
+ "token_precision": 8,
+ "token_contract": "eosio.token",
+ "txid": "6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a",
+ "created_at_block": "414927681",
+ "created_at_time": "1783387748000"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "log_global_sequence": "840242817",
+ "payout_index": 0,
+ "listing_type": "sale",
+ "listing_id": "46855",
+ "category": "attribute",
+ "collection_name": "royaltycol11",
+ "asset_id": "1099603751714",
+ "template_id": null,
+ "rule_id": "1",
+ "recipient": "jacktestr125",
+ "amount": "12500000",
+ "token_symbol": "WAX",
+ "token_precision": 8,
+ "token_contract": "eosio.token",
+ "txid": "a346ac1e0324c39a677f14dd85f23f6f94d602bf229d6810050ac3512a395a08",
+ "created_at_block": "414849702",
+ "created_at_time": "1783348758500"
+ }
+ ],
+ "query_time": 1787002593925
+ },
+ "text": "{\"success\":true,\"data\":[{\"market_contract\":\"atomicmarket\",\"log_global_sequence\":\"840330124\",\"payout_index\":0,\"listing_type\":\"sale\",\"listing_id\":\"46890\",\"category\":\"attribute\",\"collection_name\":\"royaltycol11\",\"asset_id\":\"1099603751717\",\"template_id\":null,\"rule_id\":\"2\",\"recipient\":\"jacktestr125\",\"amount\":\"1250000\",\"token_symbol\":\"WAX\",\"token_precision\":8,\"token_contract\":\"eosio.token\",\"txid\":\"6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a\",\"created_at_block\":\"414927681\",\"created_at_time\":\"1783387748000\"},{\"market_contract\":\"atomicmarket\",\"log_global_sequence\":\"840242817\",\"payout_index\":0,\"listing_type\":\"sale\",\"listing_id\":\"46855\",\"category\":\"attribute\",\"collection_name\":\"royaltycol11\",\"asset_id\":\"1099603751714\",\"template_id\":null,\"rule_id\":\"1\",\"recipient\":\"jacktestr125\",\"amount\":\"12500000\",\"token_symbol\":\"WAX\",\"token_precision\":8,\"token_contract\":\"eosio.token\",\"txid\":\"a346ac1e0324c39a677f14dd85f23f6f94d602bf229d6810050ac3512a395a08\",\"created_at_block\":\"414849702\",\"created_at_time\":\"1783348758500\"}],\"query_time\":1787002593925}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/54d685ade4a3243eb283607b3763322703a651c2.json b/packages/atomicassets/test/data/54d685ade4a3243eb283607b3763322703a651c2.json
new file mode 100644
index 00000000..8ad46f28
--- /dev/null
+++ b/packages/atomicassets/test/data/54d685ade4a3243eb283607b3763322703a651c2.json
@@ -0,0 +1,37 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/royaltycol11",
+ "params": {
+ "method": "GET",
+ "headers": {}
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": {
+ "market_contract": "atomicmarket",
+ "collection_name": "royaltycol11",
+ "founders": [
+ {
+ "weight": 1,
+ "recipient": "jacktestr125"
+ },
+ {
+ "weight": 3,
+ "recipient": "pe2etestacct"
+ }
+ ],
+ "attribute_mode": 0,
+ "split_founders": "2",
+ "split_templates": "1",
+ "split_attributes": "1",
+ "updated_at_block": "414895352",
+ "updated_at_time": "1783371583500",
+ "created_at_block": "414895352",
+ "created_at_time": "1783371583500"
+ },
+ "query_time": 1787002594513
+ },
+ "text": "{\"success\":true,\"data\":{\"market_contract\":\"atomicmarket\",\"collection_name\":\"royaltycol11\",\"founders\":[{\"weight\":1,\"recipient\":\"jacktestr125\"},{\"weight\":3,\"recipient\":\"pe2etestacct\"}],\"attribute_mode\":0,\"split_founders\":\"2\",\"split_templates\":\"1\",\"split_attributes\":\"1\",\"updated_at_block\":\"414895352\",\"updated_at_time\":\"1783371583500\",\"created_at_block\":\"414895352\",\"created_at_time\":\"1783371583500\"},\"query_time\":1787002594513}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/81dbd9169724d91c76934cae7a95530d2ffc4bf9.json b/packages/atomicassets/test/data/81dbd9169724d91c76934cae7a95530d2ffc4bf9.json
new file mode 100644
index 00000000..c152541b
--- /dev/null
+++ b/packages/atomicassets/test/data/81dbd9169724d91c76934cae7a95530d2ffc4bf9.json
@@ -0,0 +1,187 @@
+{
+ "request": {
+ "path": "https://wax-atomic.alcor.exchange/atomicmarket/v1/stats/markets",
+ "params": {
+ "method": "POST",
+ "body": "{\"symbol\":\"WAX\",\"collection_whitelist\":\"alien.worlds\",\"after\":\"1750000000000\"}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": {
+ "symbol": {
+ "token_symbol": "WAX",
+ "token_contract": "eosio.token",
+ "token_precision": 8
+ },
+ "results": [
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": null,
+ "sellers": "4896",
+ "buyers": "0",
+ "maker_volume": "1749594747693104",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "",
+ "sellers": "0",
+ "buyers": "2106",
+ "maker_volume": "0",
+ "taker_volume": "1412670696427700"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "nft.hive",
+ "sellers": "549",
+ "buyers": "1082",
+ "maker_volume": "406043235255205",
+ "taker_volume": "553583840630693"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "market.nefty",
+ "sellers": "452",
+ "buyers": "395",
+ "maker_volume": "90515525429321",
+ "taker_volume": "269335609220115"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "f12keymarket",
+ "sellers": "9",
+ "buyers": "53",
+ "maker_volume": "541643330848",
+ "taker_volume": "9862644226728"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "atomicsniper",
+ "sellers": "4",
+ "buyers": "14",
+ "maker_volume": "2934474234377",
+ "taker_volume": "2933355536600"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "market.nemo",
+ "sellers": "0",
+ "buyers": "1",
+ "maker_volume": "0",
+ "taker_volume": "5470000000000"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "bazaarbazaar",
+ "sellers": "6",
+ "buyers": "1",
+ "maker_volume": "3189822290372",
+ "taker_volume": "8131096465"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "market.wax",
+ "sellers": "44",
+ "buyers": "0",
+ "maker_volume": "555352208064",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "market.place",
+ "sellers": "1",
+ "buyers": "0",
+ "maker_volume": "300000000000",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "waxplorercom",
+ "sellers": "23",
+ "buyers": "0",
+ "maker_volume": "244857289370",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "earncashback",
+ "sellers": "0",
+ "buyers": "1",
+ "maker_volume": "0",
+ "taker_volume": "190773056817"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "alcor",
+ "sellers": "2",
+ "buyers": "0",
+ "maker_volume": "111153500000",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "waxportal",
+ "sellers": "47",
+ "buyers": "1",
+ "maker_volume": "108116880000",
+ "taker_volume": "38000000"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "hoardiostore",
+ "sellers": "0",
+ "buyers": "1",
+ "maker_volume": "0",
+ "taker_volume": "75957262807"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "kipsmarket11",
+ "sellers": "1",
+ "buyers": "1",
+ "maker_volume": "992228134",
+ "taker_volume": "12700255878"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "wax.stash",
+ "sellers": "5",
+ "buyers": "0",
+ "maker_volume": "3378000000",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "chainchampss",
+ "sellers": "1",
+ "buyers": "0",
+ "maker_volume": "424710000",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "wombatmarket",
+ "sellers": "2",
+ "buyers": "0",
+ "maker_volume": "29200000",
+ "taker_volume": "0"
+ },
+ {
+ "market_contract": "atomicmarket",
+ "marketplace_name": "rwax",
+ "sellers": "0",
+ "buyers": "1",
+ "maker_volume": "0",
+ "taker_volume": "6534992"
+ }
+ ]
+ },
+ "query_time": 1787002602663
+ },
+ "text": "{\"success\":true,\"data\":{\"symbol\":{\"token_symbol\":\"WAX\",\"token_contract\":\"eosio.token\",\"token_precision\":8},\"results\":[{\"market_contract\":\"atomicmarket\",\"marketplace_name\":null,\"sellers\":\"4896\",\"buyers\":\"0\",\"maker_volume\":\"1749594747693104\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"\",\"sellers\":\"0\",\"buyers\":\"2106\",\"maker_volume\":\"0\",\"taker_volume\":\"1412670696427700\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"nft.hive\",\"sellers\":\"549\",\"buyers\":\"1082\",\"maker_volume\":\"406043235255205\",\"taker_volume\":\"553583840630693\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"market.nefty\",\"sellers\":\"452\",\"buyers\":\"395\",\"maker_volume\":\"90515525429321\",\"taker_volume\":\"269335609220115\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"f12keymarket\",\"sellers\":\"9\",\"buyers\":\"53\",\"maker_volume\":\"541643330848\",\"taker_volume\":\"9862644226728\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"atomicsniper\",\"sellers\":\"4\",\"buyers\":\"14\",\"maker_volume\":\"2934474234377\",\"taker_volume\":\"2933355536600\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"market.nemo\",\"sellers\":\"0\",\"buyers\":\"1\",\"maker_volume\":\"0\",\"taker_volume\":\"5470000000000\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"bazaarbazaar\",\"sellers\":\"6\",\"buyers\":\"1\",\"maker_volume\":\"3189822290372\",\"taker_volume\":\"8131096465\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"market.wax\",\"sellers\":\"44\",\"buyers\":\"0\",\"maker_volume\":\"555352208064\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"market.place\",\"sellers\":\"1\",\"buyers\":\"0\",\"maker_volume\":\"300000000000\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"waxplorercom\",\"sellers\":\"23\",\"buyers\":\"0\",\"maker_volume\":\"244857289370\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"earncashback\",\"sellers\":\"0\",\"buyers\":\"1\",\"maker_volume\":\"0\",\"taker_volume\":\"190773056817\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"alcor\",\"sellers\":\"2\",\"buyers\":\"0\",\"maker_volume\":\"111153500000\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"waxportal\",\"sellers\":\"47\",\"buyers\":\"1\",\"maker_volume\":\"108116880000\",\"taker_volume\":\"38000000\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"hoardiostore\",\"sellers\":\"0\",\"buyers\":\"1\",\"maker_volume\":\"0\",\"taker_volume\":\"75957262807\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"kipsmarket11\",\"sellers\":\"1\",\"buyers\":\"1\",\"maker_volume\":\"992228134\",\"taker_volume\":\"12700255878\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"wax.stash\",\"sellers\":\"5\",\"buyers\":\"0\",\"maker_volume\":\"3378000000\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"chainchampss\",\"sellers\":\"1\",\"buyers\":\"0\",\"maker_volume\":\"424710000\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"wombatmarket\",\"sellers\":\"2\",\"buyers\":\"0\",\"maker_volume\":\"29200000\",\"taker_volume\":\"0\"},{\"market_contract\":\"atomicmarket\",\"marketplace_name\":\"rwax\",\"sellers\":\"0\",\"buyers\":\"1\",\"maker_volume\":\"0\",\"taker_volume\":\"6534992\"}]},\"query_time\":1787002602663}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/8c1b9d13567a6f1f07fbff1819392a88e675587e.json b/packages/atomicassets/test/data/8c1b9d13567a6f1f07fbff1819392a88e675587e.json
new file mode 100644
index 00000000..80ea5ba0
--- /dev/null
+++ b/packages/atomicassets/test/data/8c1b9d13567a6f1f07fbff1819392a88e675587e.json
@@ -0,0 +1,27 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/accounts/jacktestr125",
+ "params": {
+ "method": "POST",
+ "body": "{}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": [
+ {
+ "token_symbol": "WAX",
+ "token_precision": 8,
+ "token_contract": "eosio.token",
+ "amount": "76875000",
+ "payout_count": "10"
+ }
+ ],
+ "query_time": 1787002594271
+ },
+ "text": "{\"success\":true,\"data\":[{\"token_symbol\":\"WAX\",\"token_precision\":8,\"token_contract\":\"eosio.token\",\"amount\":\"76875000\",\"payout_count\":\"10\"}],\"query_time\":1787002594271}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/b79d4f7c4eff7d9021f8e2ebfb85622dcf85cc96.json b/packages/atomicassets/test/data/b79d4f7c4eff7d9021f8e2ebfb85622dcf85cc96.json
new file mode 100644
index 00000000..5177ba89
--- /dev/null
+++ b/packages/atomicassets/test/data/b79d4f7c4eff7d9021f8e2ebfb85622dcf85cc96.json
@@ -0,0 +1,43 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/royaltycol11/attributes",
+ "params": {
+ "method": "POST",
+ "body": "{}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": [
+ {
+ "market_contract": "atomicmarket",
+ "collection_name": "royaltycol11",
+ "rule_id": "2",
+ "source": 0,
+ "field": "rarity",
+ "value": [
+ "string",
+ "legendary"
+ ],
+ "weight": "1",
+ "recipients": [
+ {
+ "weight": 1,
+ "recipient": "jacktestr125"
+ }
+ ],
+ "lookup_hash": "68ec3427c453d24cfd9faaa1db9d39533ab06b8127bb09a6f2958e5c26ebce74",
+ "updated_at_block": "414895354",
+ "updated_at_time": "1783371584500",
+ "created_at_block": "414895354",
+ "created_at_time": "1783371584500"
+ }
+ ],
+ "query_time": 1787002595151
+ },
+ "text": "{\"success\":true,\"data\":[{\"market_contract\":\"atomicmarket\",\"collection_name\":\"royaltycol11\",\"rule_id\":\"2\",\"source\":0,\"field\":\"rarity\",\"value\":[\"string\",\"legendary\"],\"weight\":\"1\",\"recipients\":[{\"weight\":1,\"recipient\":\"jacktestr125\"}],\"lookup_hash\":\"68ec3427c453d24cfd9faaa1db9d39533ab06b8127bb09a6f2958e5c26ebce74\",\"updated_at_block\":\"414895354\",\"updated_at_time\":\"1783371584500\",\"created_at_block\":\"414895354\",\"created_at_time\":\"1783371584500\"}],\"query_time\":1787002595151}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/d99acc6aba98e145572720714fb869b5971f917a.json b/packages/atomicassets/test/data/d99acc6aba98e145572720714fb869b5971f917a.json
new file mode 100644
index 00000000..0e9b925e
--- /dev/null
+++ b/packages/atomicassets/test/data/d99acc6aba98e145572720714fb869b5971f917a.json
@@ -0,0 +1,19 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/payouts/_count",
+ "params": {
+ "method": "POST",
+ "body": "{}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
+ },
+ "status": 200,
+ "json": {
+ "success": true,
+ "data": "20",
+ "query_time": 1787002594102
+ },
+ "text": "{\"success\":true,\"data\":\"20\",\"query_time\":1787002594102}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/data/e3758d05e6df3731ff638d315d58f8110ab96876.json b/packages/atomicassets/test/data/e3758d05e6df3731ff638d315d58f8110ab96876.json
new file mode 100644
index 00000000..6e4b39db
--- /dev/null
+++ b/packages/atomicassets/test/data/e3758d05e6df3731ff638d315d58f8110ab96876.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "path": "https://test.wax.api.atomicassets.io/atomicmarket/v1/royalties/alien.worlds",
+ "params": {
+ "method": "GET",
+ "headers": {}
+ }
+ },
+ "status": 416,
+ "json": {
+ "success": false,
+ "message": "Royalty config not found"
+ },
+ "text": "{\"success\":false,\"message\":\"Royalty config not found\"}"
+}
\ No newline at end of file
diff --git a/packages/atomicassets/test/royalties-api.ts b/packages/atomicassets/test/royalties-api.ts
new file mode 100644
index 00000000..6d817181
--- /dev/null
+++ b/packages/atomicassets/test/royalties-api.ts
@@ -0,0 +1,97 @@
+import {assert} from 'chai'
+
+import {APIClient, APIError, FetchProvider} from '@wharfkit/antelope'
+import {mockFetch} from '@wharfkit/mock-data'
+import {V2_BASE_URL, TIMEOUT, SLOW_THRESHOLD} from './config'
+
+import {AtomicAssetsAPIClient, Types} from '$lib'
+
+// The royalty routes answer with rows only on an AtomicMarket v2 chain, so this
+// client points at the testnet indexer instead of the shared BASE_URL.
+const client = new APIClient({
+ provider: new FetchProvider(V2_BASE_URL, {fetch: mockFetch}),
+})
+
+// Setup the API
+const atomicassets = new AtomicAssetsAPIClient(client)
+
+const collectionName = 'royaltycol11'
+const unconfiguredCollection = 'alien.worlds'
+const recipient = 'jacktestr125'
+
+suite('atomicmarket royalties', function () {
+ this.slow(SLOW_THRESHOLD)
+ this.timeout(TIMEOUT)
+
+ test('get_royalty_payouts', async function () {
+ const res = await atomicassets.atomicmarket.v1.get_royalty_payouts({limit: 2})
+ assert.instanceOf(res, Types.Market.GetRoyaltyPayoutsResponse)
+ assert.equal(res.success, true)
+ assert.isNotEmpty(res.data)
+ })
+
+ test('get_royalty_payouts filtered', async function () {
+ const res = await atomicassets.atomicmarket.v1.get_royalty_payouts({
+ category: ['attribute'],
+ listing_type: 'sale',
+ sort: 'amount',
+ order: 'asc',
+ limit: 2,
+ })
+ assert.instanceOf(res, Types.Market.GetRoyaltyPayoutsResponse)
+ assert.equal(res.success, true)
+ assert.isNotEmpty(res.data)
+ })
+
+ test('get_royalty_payouts_count', async function () {
+ const res = await atomicassets.atomicmarket.v1.get_royalty_payouts_count()
+ assert.instanceOf(res, Types.CountResponseStruct)
+ assert.equal(res.success, true)
+ assert.isAbove(res.data.toNumber(), 0)
+ })
+
+ test('get_royalty_account', async function () {
+ const res = await atomicassets.atomicmarket.v1.get_royalty_account(recipient)
+ assert.instanceOf(res, Types.Market.GetRoyaltyAccountResponse)
+ assert.equal(res.success, true)
+ assert.isNotEmpty(res.data)
+ assert.isAbove(res.data[0].payout_count.toNumber(), 0)
+ })
+
+ test('get_royalty_config', async function () {
+ const res = await atomicassets.atomicmarket.v1.get_royalty_config(collectionName)
+ assert.instanceOf(res, Types.Market.GetRoyaltyConfigResponse)
+ assert.equal(res.success, true)
+ assert.isNotEmpty(res.data)
+ assert.isTrue(res.data.collection_name.equals(collectionName))
+ })
+
+ test('get_royalty_config answers 416 for an unconfigured collection', async function () {
+ // A collection with no royalty configuration is not a failure for the
+ // caller, and on an AtomicMarket v1 chain every collection answers so.
+ let error: APIError | undefined
+
+ try {
+ await atomicassets.atomicmarket.v1.get_royalty_config(unconfiguredCollection)
+ } catch (caught) {
+ error = caught as APIError
+ }
+
+ assert.instanceOf(error, APIError)
+ assert.equal(error?.response.status, 416)
+ })
+
+ test('get_royalty_template_rules', async function () {
+ const res = await atomicassets.atomicmarket.v1.get_royalty_template_rules(collectionName)
+ assert.instanceOf(res, Types.Market.GetRoyaltyTemplateRulesResponse)
+ assert.equal(res.success, true)
+ assert.isNotEmpty(res.data)
+ })
+
+ test('get_royalty_attribute_rules', async function () {
+ const res = await atomicassets.atomicmarket.v1.get_royalty_attribute_rules(collectionName)
+ assert.instanceOf(res, Types.Market.GetRoyaltyAttributeRulesResponse)
+ assert.equal(res.success, true)
+ assert.isNotEmpty(res.data)
+ })
+})
diff --git a/packages/atomicassets/test/schema.ts b/packages/atomicassets/test/schema.ts
index b40eab25..b5bc81e3 100644
--- a/packages/atomicassets/test/schema.ts
+++ b/packages/atomicassets/test/schema.ts
@@ -5,8 +5,15 @@ import {mockFetch} from '@wharfkit/mock-data'
import {PlaceholderAuth} from '@wharfkit/signing-request'
import {BASE_URL, TIMEOUT, SLOW_THRESHOLD} from './config'
-import type {Schema} from '$lib'
-import {AtomicAssetsAPIClient, AtomicAssetsContract, AtomicAssetsKit, KitUtility, Types} from '$lib'
+import {
+ AtomicAssetsAPIClient,
+ AtomicAssetsContract,
+ AtomicAssetsKit,
+ AtomicMarketContract,
+ KitUtility,
+ Schema,
+ Types,
+} from '$lib'
const client = new APIClient({
provider: new FetchProvider(Chains.WAX.url, {fetch: mockFetch}),
@@ -78,6 +85,38 @@ suite('Schema', function () {
assert.isNull(field.info)
})
+ test('types returns the authored media type descriptors', function () {
+ // The shared fixture comes from a v1 chain, whose schema endpoints
+ // report no types at all, so the descriptors are built here instead.
+ const schemaObject = Types.SchemaObject.from({
+ schema_name: schemaName,
+ format: [{name: 'video', type: 'string', mediatype: 'video/mp4'}],
+ types: [{name: 'video', mediatype: 'video/mp4', info: 'trailer'}],
+ created_at_block: 1,
+ created_at_time: '1',
+ })
+ const schema = Schema.from(schemaObject, utility)
+
+ assert.instanceOf(schema.types[0], Types.SchemaFormatType)
+ assert.equal(schema.types[0].name, 'video')
+ assert.equal(schema.types[0].mediatype, 'video/mp4')
+ assert.equal(schema.types[0].info, 'trailer')
+ })
+
+ test('the API royalty pair stays separate from the contract royalty pair', function () {
+ // The contract's ROYALTYPAIR is what setroyalconf and the rule actions
+ // serialize. The API struct only decodes a response row, and keeping the
+ // two apart is what stops a decoded row from reaching action data.
+ const contractFields = AtomicMarketContract.Types.ROYALTYPAIR.abiFields?.map((f) => f.name)
+ const apiFields = Types.RoyaltyPair.abiFields?.map((f) => f.name)
+
+ assert.deepEqual(contractFields, ['recipient', 'weight'])
+ assert.deepEqual(apiFields, ['recipient', 'weight'])
+ assert.notEqual(Types.RoyaltyPair, AtomicMarketContract.Types.ROYALTYPAIR as any)
+ assert.equal(AtomicMarketContract.Types.ROYALTYPAIR.abiName, 'ROYALTYPAIR')
+ assert.equal(Types.RoyaltyPair.abiName, 'royalty_pair')
+ })
+
test('the API format type stays separate from the contract format type', function () {
// The contract's FORMAT is what createschema and extendschema serialize,
// so it must stay at {name, type}. The API reports two further fields.
diff --git a/packages/atomicassets/test/utils.ts b/packages/atomicassets/test/utils.ts
new file mode 100644
index 00000000..e7e4c583
--- /dev/null
+++ b/packages/atomicassets/test/utils.ts
@@ -0,0 +1,53 @@
+import {assert} from 'chai'
+import {APIClient, FetchProvider, Name, UInt64} from '@wharfkit/antelope'
+import {mockFetch} from '@wharfkit/mock-data'
+import {BASE_URL, TIMEOUT, SLOW_THRESHOLD} from './config'
+
+import {AtomicAssetsAPIClient} from '$lib'
+import {pathSegment} from '../src/endpoints/utils'
+
+// Setup the API
+const atomicassets = new AtomicAssetsAPIClient(
+ new APIClient({
+ provider: new FetchProvider(BASE_URL, {fetch: mockFetch}),
+ })
+)
+
+suite('utils', function () {
+ this.slow(SLOW_THRESHOLD)
+ this.timeout(TIMEOUT)
+
+ test('pathSegment encodes characters that rewrite the path', function () {
+ assert.equal(pathSegment('alien/worlds'), 'alien%2Fworlds')
+ assert.equal(pathSegment('alien?worlds'), 'alien%3Fworlds')
+ assert.equal(pathSegment('alien#worlds'), 'alien%23worlds')
+ assert.equal(pathSegment('alien worlds'), 'alien%20worlds')
+ assert.equal(pathSegment('alien&worlds'), 'alien%26worlds')
+ })
+
+ test('pathSegment returns the text of a Name and a UInt64', function () {
+ assert.equal(pathSegment(Name.from('alice')), 'alice')
+ assert.equal(pathSegment(UInt64.from(5)), '5')
+ })
+
+ test('pathSegment rejects an empty value and the dot segments', function () {
+ assert.throws(() => pathSegment(''), /Invalid path segment/)
+ assert.throws(() => pathSegment('.'), /Invalid path segment/)
+ assert.throws(() => pathSegment('..'), /Invalid path segment/)
+ assert.throws(() => pathSegment(null), /a value is required/)
+ assert.throws(() => pathSegment(undefined), /a value is required/)
+ })
+
+ test('get_asset rejects a dot segment before it requests anything', async function () {
+ let error: unknown
+
+ try {
+ await atomicassets.atomicassets.v1.get_asset('..')
+ } catch (caught) {
+ error = caught
+ }
+
+ assert.instanceOf(error, Error)
+ assert.match((error as Error).message, /^Invalid path segment '\.\.'/)
+ })
+})
diff --git a/packages/atomicassets/test/v2-api.ts b/packages/atomicassets/test/v2-api.ts
index a85c7740..13edc783 100644
--- a/packages/atomicassets/test/v2-api.ts
+++ b/packages/atomicassets/test/v2-api.ts
@@ -66,4 +66,265 @@ suite('v2 API response fields', function () {
assert.isTrue(collection.new_author_name.equals('bob'))
assert.equal(collection.new_author_date, '1785263212000')
})
+
+ // The nested collection carries the fee the listing was created with, and
+ // current_collection_fee carries the fee as of the last indexed block.
+ const listingCollection = {
+ collection_name: 'royaltycol11',
+ author: 'alice',
+ allow_notify: true,
+ authorized_accounts: [],
+ notify_accounts: [],
+ market_fee: 0.05,
+ created_at_block: 1,
+ created_at_time: '1',
+ }
+
+ const listingPrice = {
+ token_contract: 'eosio.token',
+ token_symbol: 'WAX',
+ token_precision: 8,
+ amount: '1250000',
+ }
+
+ const listingTemplate = {
+ template_id: 662912,
+ is_transferable: true,
+ is_burnable: true,
+ issued_supply: 1,
+ max_supply: 10,
+ immutable_data: {name: 'Test'},
+ created_at_block: 1,
+ created_at_time: '1',
+ }
+
+ test('auctions, buyoffers, and template buyoffers report the live collection fee', function () {
+ const auction = Types.AuctionObject.from({
+ market_contract: 'atomicmarket',
+ assets_contract: 'atomicassets',
+ auction_id: 1,
+ seller: 'alice',
+ assets: [],
+ end_time: '1783387748000',
+ price: listingPrice,
+ bids: [],
+ state: 1,
+ claimed_by_seller: false,
+ claimed_by_buyer: false,
+ collection: listingCollection,
+ is_seller_contract: false,
+ created_at_block: 1,
+ created_at_time: '1',
+ updated_at_block: 2,
+ updated_at_time: '2',
+ current_collection_fee: 0.07,
+ })
+
+ const buyoffer = Types.BuyofferObject.from({
+ market_contract: 'atomicmarket',
+ assets_contract: 'atomicassets',
+ buyoffer_id: 2,
+ seller: 'alice',
+ buyer: 'bob',
+ price: listingPrice,
+ assets: [],
+ collection: listingCollection,
+ memo: '',
+ created_at_block: 1,
+ created_at_time: '1',
+ updated_at_block: 2,
+ updated_at_time: '2',
+ state: 0,
+ current_collection_fee: 0.07,
+ })
+
+ const templateBuyoffer = Types.TemplateBuyofferObject.from({
+ market_contract: 'atomicmarket',
+ assets_contract: 'atomicassets',
+ buyoffer_id: 3,
+ buyer: 'bob',
+ price: listingPrice,
+ assets: [],
+ collection: listingCollection,
+ template: listingTemplate,
+ created_at_block: 1,
+ created_at_time: '1',
+ updated_at_block: 2,
+ updated_at_time: '2',
+ state: 0,
+ current_collection_fee: 0.07,
+ })
+
+ assert.equal(auction.current_collection_fee.value, 0.07)
+ assert.equal(buyoffer.current_collection_fee.value, 0.07)
+ assert.equal(templateBuyoffer.current_collection_fee.value, 0.07)
+
+ assert.equal(auction.collection.market_fee.value, 0.05)
+ assert.equal(buyoffer.collection.market_fee.value, 0.05)
+ assert.equal(templateBuyoffer.collection.market_fee.value, 0.05)
+ })
+
+ test('an auction from a v1 indexer decodes without the collection fee', function () {
+ const auction = Types.AuctionObject.from({
+ market_contract: 'atomicmarket',
+ assets_contract: 'atomicassets',
+ auction_id: 1,
+ seller: 'alice',
+ assets: [],
+ end_time: '1783387748000',
+ price: listingPrice,
+ bids: [],
+ state: 1,
+ claimed_by_seller: false,
+ claimed_by_buyer: false,
+ collection: listingCollection,
+ is_seller_contract: false,
+ created_at_block: 1,
+ created_at_time: '1',
+ updated_at_block: 2,
+ updated_at_time: '2',
+ })
+
+ assert.isUndefined(auction.current_collection_fee)
+ assert.equal(auction.collection.market_fee.value, 0.05)
+ })
+
+ test('a schema reports the authored media type descriptors', function () {
+ const schema = Types.SchemaObject.from({
+ schema_name: 'cmbz.res',
+ format: [{name: 'video', type: 'string', mediatype: 'video/mp4', info: 'trailer'}],
+ types: [{name: 'video', mediatype: 'video/mp4', info: 'trailer'}],
+ created_at_block: 1,
+ created_at_time: '1',
+ })
+
+ assert.equal(schema.types.length, 1)
+ assert.instanceOf(schema.types[0], Types.SchemaFormatType)
+ assert.equal(schema.types[0].name, 'video')
+ assert.equal(schema.types[0].mediatype, 'video/mp4')
+ assert.equal(schema.types[0].info, 'trailer')
+ })
+
+ test('a schema without the descriptors decodes and reports none', function () {
+ const schema = Types.SchemaObject.from({
+ schema_name: 'cmbz.res',
+ format: [{name: 'video', type: 'string'}],
+ created_at_block: 1,
+ created_at_time: '1',
+ })
+
+ // An absent types array means the response does not report descriptors,
+ // which is a different answer from the empty array a schema endpoint
+ // sends for a schema that has none.
+ assert.isUndefined(schema.types)
+ })
+
+ test('a royalty payout decodes the nullable ids the indexer emits', function () {
+ const attributePayout = Types.RoyaltyPayout.from({
+ market_contract: 'atomicmarket',
+ log_global_sequence: '840330124',
+ payout_index: 0,
+ listing_type: 'sale',
+ listing_id: '46890',
+ category: 'attribute',
+ collection_name: 'royaltycol11',
+ asset_id: '1099603751717',
+ template_id: null,
+ rule_id: '2',
+ recipient: 'jacktestr125',
+ amount: '1250000',
+ token_symbol: 'WAX',
+ token_precision: 8,
+ token_contract: 'eosio.token',
+ txid: '6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a',
+ created_at_block: '414927681',
+ created_at_time: '1783387748000',
+ })
+
+ assert.equal(attributePayout.amount.toNumber(), 1250000)
+ assert.equal(attributePayout.listing_id.toNumber(), 46890)
+ assert.equal(attributePayout.rule_id.toNumber(), 2)
+ assert.isUndefined(attributePayout.template_id)
+
+ // listing_type and category are the strings the indexer maps the stored
+ // integers to, and it sends null for a value it has no name for.
+ const unmappedPayout = Types.RoyaltyPayout.from({
+ market_contract: 'atomicmarket',
+ log_global_sequence: '840330125',
+ payout_index: 1,
+ listing_type: null,
+ listing_id: null,
+ category: null,
+ collection_name: 'royaltycol11',
+ asset_id: null,
+ template_id: null,
+ rule_id: null,
+ recipient: 'jacktestr125',
+ amount: '1250000',
+ token_symbol: 'WAX',
+ token_precision: 8,
+ token_contract: 'eosio.token',
+ txid: '6df6118add83bab9b0d79b5a7a6cf3877133893daefb96280f6f52943db74f2a',
+ created_at_block: '414927681',
+ created_at_time: '1783387748000',
+ })
+
+ assert.isNull(unmappedPayout.listing_type)
+ assert.isNull(unmappedPayout.category)
+ assert.isUndefined(unmappedPayout.listing_id)
+ assert.isUndefined(unmappedPayout.asset_id)
+ })
+
+ test('a royalty attribute rule keeps the raw variant tuple', function () {
+ const rule = Types.RoyaltyAttributeRule.from({
+ market_contract: 'atomicmarket',
+ collection_name: 'royaltycol11',
+ rule_id: '2',
+ source: 0,
+ field: 'rarity',
+ value: ['string', 'legendary'],
+ weight: '1',
+ recipients: [{recipient: 'jacktestr125', weight: 1}],
+ lookup_hash: '68ec3427c453d24cfd9faaa1db9d39533ab06b8127bb09a6f2958e5c26ebce74',
+ updated_at_block: '414895354',
+ updated_at_time: '1783371584500',
+ created_at_block: '414895354',
+ created_at_time: '1783371584500',
+ })
+
+ assert.deepEqual(rule.value, ['string', 'legendary'])
+ assert.equal(rule.weight.toNumber(), 1)
+ assert.isTrue(rule.recipients[0].recipient.equals('jacktestr125'))
+ })
+
+ test('a marketplace stat decodes an unnamed marketplace and empty volumes', function () {
+ const unnamed = Types.Market.MarketplaceStat.from({
+ market_contract: 'atomicmarket',
+ marketplace_name: null,
+ sellers: '4896',
+ buyers: '0',
+ maker_volume: '1749594747693104',
+ taker_volume: null,
+ })
+
+ // Null when no marketplace is stored, and the empty string when the
+ // stored value is the default marketplace name. Both mean the same
+ // thing to a client.
+ const blank = Types.Market.MarketplaceStat.from({
+ market_contract: 'atomicmarket',
+ marketplace_name: '',
+ sellers: '0',
+ buyers: '2106',
+ maker_volume: null,
+ taker_volume: '1412670696427700',
+ })
+
+ assert.isNull(unnamed.marketplace_name)
+ assert.equal(unnamed.sellers.toNumber(), 4896)
+ assert.isUndefined(unnamed.taker_volume)
+
+ assert.equal(blank.marketplace_name, '')
+ assert.isUndefined(blank.maker_volume)
+ assert.equal(blank.taker_volume.toNumber(), 1412670696427700)
+ })
})
diff --git a/packages/bundle/README.md b/packages/bundle/README.md
index 8381b755..432aec5f 100644
--- a/packages/bundle/README.md
+++ b/packages/bundle/README.md
@@ -5,7 +5,7 @@ A prepackaged bundle of common Wharf libraries, built as a self-contained IIFE (
## Usage
```html
-
+
```
@@ -30,7 +30,7 @@ Both URLs above name an exact version and the full file path. jsDelivr serves th
Use `https://cdn.jsdelivr.net/npm/@wharfkit/bundle@4/dist/wharf.bundle.js` if you want patch releases without editing the page. The trade is that the bytes are no longer the package's own and no longer eligible for Subresource Integrity.
-To add an `integrity` attribute, take the hash for the exact version from `https://data.jsdelivr.com/v1/packages/npm/@wharfkit/bundle@4.0.0?structure=flat`, and pair it with `crossorigin="anonymous"`, which SRI requires. An `integrity` attribute on a `
```
-Mixing versions across imports, or adding a package still on the `1.x` line, splits it again. esm.sh takes an explicit pin against that: `?deps=@wharfkit/antelope@4.0.0` on each top-level URL rewrites the antelope import to one concrete build and propagates into every transitive `@wharfkit/*` request, so it costs one query parameter per import rather than one entry per transitive package. jsDelivr's `+esm` route has no query-parameter equivalent, and its output carries jsDelivr's own warning against pairing it with Subresource Integrity.
+Mixing versions across imports, or adding a package still on the `1.x` line, splits it again. esm.sh takes an explicit pin against that: `?deps=@wharfkit/antelope@4.0.1` on each top-level URL rewrites the antelope import to one concrete build and propagates into every transitive `@wharfkit/*` request, so it costs one query parameter per import rather than one entry per transitive package. jsDelivr's `+esm` route has no query-parameter equivalent, and its output carries jsDelivr's own warning against pairing it with Subresource Integrity.
The bundle stays the recommended browser artifact. It is the only one that guarantees a single antelope without a resolver.
## Examples
-`public/bundle.html` and `public/esm.html` are runnable examples of both forms, loading the built files next to them. `make` copies them into `dist/`, so open them from there after a build. The same pages ship in the package, at `https://cdn.jsdelivr.net/npm/@wharfkit/bundle@4.0.0/dist/bundle.html` and `.../dist/esm.html`.
+`public/bundle.html` and `public/esm.html` are runnable examples of both forms, loading the built files next to them. `make` copies them into `dist/`, so open them from there after a build. The same pages ship in the package, at `https://cdn.jsdelivr.net/npm/@wharfkit/bundle@4.0.1/dist/bundle.html` and `.../dist/esm.html`.
## Types
diff --git a/packages/bundle/package.json b/packages/bundle/package.json
index 762ae55a..894ad132 100644
--- a/packages/bundle/package.json
+++ b/packages/bundle/package.json
@@ -1,6 +1,6 @@
{
"name": "@wharfkit/bundle",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"description": "A prepackaged bundle of common Wharf libraries re-exported for IIFE or ESM",
"license": "BSD-3-Clause",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 00f0880f..149ecf13 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@wharfkit/cli",
- "version": "4.0.0",
+ "version": "4.0.1",
"license": "BSD-3-Clause",
"homepage": "https://wharfkit.com/docs/utilities/cli",
"description": "Command line utilities for Wharf",
diff --git a/packages/common/package.json b/packages/common/package.json
index 435bd68f..9fd8e8ea 100644
--- a/packages/common/package.json
+++ b/packages/common/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/common",
"description": "Common data and functions shared across WharfKit packages",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/docs/utilities/common-library",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/conformance/package.json b/packages/conformance/package.json
index 69bd7d33..a8917e35 100644
--- a/packages/conformance/package.json
+++ b/packages/conformance/package.json
@@ -1,6 +1,6 @@
{
"name": "@wharfkit/conformance",
- "version": "4.0.0",
+ "version": "4.0.1",
"type": "module",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/contract/package.json b/packages/contract/package.json
index 4726b64f..f9b05567 100644
--- a/packages/contract/package.json
+++ b/packages/contract/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/contract",
"description": "ContractKit for Wharf",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/docs/contract-kit",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/contract/src/contract/scope-cursor.ts b/packages/contract/src/contract/scope-cursor.ts
index eace9bce..17d47293 100644
--- a/packages/contract/src/contract/scope-cursor.ts
+++ b/packages/contract/src/contract/scope-cursor.ts
@@ -37,8 +37,12 @@ export class TableScopeCursor extends TableCursor {
code: this.params.code,
table: this.params.table,
limit,
- lower_bound: lower_bound ? String(lower_bound) : undefined,
- upper_bound: this.params.upper_bound ? String(this.params.upper_bound) : undefined,
+ lower_bound:
+ lower_bound === undefined || lower_bound === null ? undefined : String(lower_bound),
+ upper_bound:
+ this.params.upper_bound === undefined || this.params.upper_bound === null
+ ? undefined
+ : String(this.params.upper_bound),
}
const result = await this.client!.v1.chain.get_table_by_scope(query)
diff --git a/packages/contract/test/tests/table.ts b/packages/contract/test/tests/table.ts
index b57ba2c1..069fef19 100644
--- a/packages/contract/test/tests/table.ts
+++ b/packages/contract/test/tests/table.ts
@@ -729,5 +729,32 @@ suite('Table', () => {
)
})
})
+
+ test('should send a bound of zero', async function () {
+ const queries: any[] = []
+ const client = new APIClient({
+ provider: {
+ call: async ({params}: any) => {
+ queries.push(JSON.parse(JSON.stringify(params)))
+ return {
+ status: 200,
+ headers: {},
+ text: '{"rows":[],"more":""}',
+ json: {rows: [], more: ''},
+ }
+ },
+ },
+ })
+ const cursor = new TableScopeCursor({
+ abi: msig.abi,
+ client,
+ params: {code: 'eosio.msig', table: 'proposal', lower_bound: 0, upper_bound: 0},
+ })
+
+ await cursor.next()
+
+ assert.equal(queries[0].lower_bound, '0')
+ assert.equal(queries[0].upper_bound, '0')
+ })
})
})
diff --git a/packages/hyperion/package.json b/packages/hyperion/package.json
index b97ebaa8..00d3e02d 100644
--- a/packages/hyperion/package.json
+++ b/packages/hyperion/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/hyperion",
"description": "API Client to access Hyperion API endpoints",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/light-api/package.json b/packages/light-api/package.json
index 73be129d..99792eb8 100644
--- a/packages/light-api/package.json
+++ b/packages/light-api/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/light-api",
"description": "API Client to access Light API endpoints",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/mock-data/package.json b/packages/mock-data/package.json
index 4e98cb0c..312bfdb1 100644
--- a/packages/mock-data/package.json
+++ b/packages/mock-data/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/mock-data",
"description": "Sample data for usage in tests throughout @wharfkit",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/msigs/package.json b/packages/msigs/package.json
index 46849f61..fec46193 100644
--- a/packages/msigs/package.json
+++ b/packages/msigs/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/msigs",
"description": "API Client to access Roborovski msigs API endpoints",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/protocol-esr/package.json b/packages/protocol-esr/package.json
index ade28985..3ddb52b0 100644
--- a/packages/protocol-esr/package.json
+++ b/packages/protocol-esr/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/protocol-esr",
"description": "Abstract methods useful to all ESR-based wallet plugins",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/protocol-scatter/package.json b/packages/protocol-scatter/package.json
index 6d20891c..2d0b1007 100644
--- a/packages/protocol-scatter/package.json
+++ b/packages/protocol-scatter/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/protocol-scatter",
"description": "Abstract methods useful to all Scatter-based wallet plugins",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/resources/package.json b/packages/resources/package.json
index b07d38d2..79315c7f 100644
--- a/packages/resources/package.json
+++ b/packages/resources/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/resources",
"description": "Library to assist in Antelope-blockchain resource calculations.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/roborovski/package.json b/packages/roborovski/package.json
index 361b8f33..a3aeb583 100644
--- a/packages/roborovski/package.json
+++ b/packages/roborovski/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/roborovski",
"description": "API Client to access Roborovski API endpoints",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/sealed-messages/package.json b/packages/sealed-messages/package.json
index b49dcdcf..1576871e 100644
--- a/packages/sealed-messages/package.json
+++ b/packages/sealed-messages/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/sealed-messages",
"description": "",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/sealed-messages/src/index.ts b/packages/sealed-messages/src/index.ts
index 41d23beb..db78a0a5 100644
--- a/packages/sealed-messages/src/index.ts
+++ b/packages/sealed-messages/src/index.ts
@@ -58,7 +58,7 @@ export function sealMessage(
publicKey: PublicKey,
nonce: UInt64
): Bytes {
- const secret = privateKey.sharedSecret(publicKey)
+ const secret = privateKey.sharedSecret(publicKey, {legacy: true})
const iv = createIV(nonce, secret)
const symmetricKey = createSymmetricKey(secret, nonce)
return encryptMessage(iv, symmetricKey, message)
@@ -73,7 +73,7 @@ export function sealedMessagePayload(
if (!nonce) {
nonce = UInt64.random()
}
- const secret = privateKey.sharedSecret(publicKey)
+ const secret = privateKey.sharedSecret(publicKey, {legacy: true})
const iv = createIV(nonce, secret)
const symmetricKey = createSymmetricKey(secret, nonce)
const ciphertext = encryptMessage(iv, symmetricKey, message)
@@ -102,7 +102,7 @@ export function unsealMessage(
publicKey: PublicKey,
nonce: UInt64
): string {
- const secret = privateKey.sharedSecret(publicKey)
+ const secret = privateKey.sharedSecret(publicKey, {legacy: true})
const iv = createIV(nonce, secret)
const symmetricKey = createSymmetricKey(secret, nonce)
const decryptedMessage = decryptMessage(iv, symmetricKey, message)
diff --git a/packages/sealed-messages/test/tests/utils.ts b/packages/sealed-messages/test/tests/utils.ts
index 442d539b..4181814e 100644
--- a/packages/sealed-messages/test/tests/utils.ts
+++ b/packages/sealed-messages/test/tests/utils.ts
@@ -1,6 +1,13 @@
import {assert} from 'chai'
-import {PrivateKey, UInt64} from '@wharfkit/antelope'
-import {sealMessage, unsealMessage} from '../../src/index'
+import {Checksum256, PrivateKey, UInt64} from '@wharfkit/antelope'
+import {
+ createIV,
+ createSymmetricKey,
+ encryptMessage,
+ sealedMessagePayload,
+ sealMessage,
+ unsealMessage,
+} from '../../src/index'
suite('utils', function () {
test('sealMessage', function () {
@@ -26,4 +33,37 @@ suite('utils', function () {
const unsealedMessage = unsealMessage(sealedMessage, to, from.toPublic(), nonce)
assert.equal(unsealedMessage, message)
})
+
+ suite('shared secret with a leading zero byte', function () {
+ // ECDH x-coordinate of this pair starts with 0x00
+ const from = PrivateKey.from('5KZJED2ubZVe7L5Xw7u7sb6pktpaoT69rxGRjQT6PKrDRzp9Gu8')
+ const to = PrivateKey.from('5Ke897mHoFzE1mTSk4j8KCxrsSRyzQZCqYPGv1rahh39XVpqcTL')
+ const nonce = UInt64.from(1234567890)
+ const message = 'Hello, World!'
+
+ test('seals with the stripped derivation', function () {
+ const payload = sealedMessagePayload(message, from, to.toPublic(), nonce)
+ assert.equal(payload.ciphertext.hexString, '51686e0c16836c4d30b8bb0938e4cfe5')
+ assert.equal(Number(payload.checksum), 3865664645)
+ assert.equal(String(payload.from), String(from.toPublic()))
+ })
+
+ test('differs from the 32-byte derivation', function () {
+ const secret = from.sharedSecret(to.toPublic())
+ const iv = createIV(nonce, secret)
+ const ciphertext = encryptMessage(iv, createSymmetricKey(secret, nonce), message)
+ const checksum = new DataView(Checksum256.hash(iv.array).array.buffer).getUint32(
+ 0,
+ true
+ )
+ const payload = sealedMessagePayload(message, from, to.toPublic(), nonce)
+ assert.notEqual(payload.ciphertext.hexString, ciphertext.hexString)
+ assert.notEqual(Number(payload.checksum), checksum)
+ })
+
+ test('unseals the stripped derivation', function () {
+ const payload = sealedMessagePayload(message, from, to.toPublic(), nonce)
+ assert.equal(unsealMessage(payload.ciphertext, to, payload.from, nonce), message)
+ })
+ })
})
diff --git a/packages/session/package.json b/packages/session/package.json
index 0a53693c..b6849884 100644
--- a/packages/session/package.json
+++ b/packages/session/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/session",
"description": "Create account-based sessions, perform transactions, and allow users to login using Antelope-based blockchains.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/docs/session-kit",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/session/src/encoded.ts b/packages/session/src/encoded.ts
deleted file mode 100644
index 5fc722a1..00000000
--- a/packages/session/src/encoded.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import {Checksum256, Name, Struct} from '@wharfkit/antelope'
-import {SerializedSession, Session, SessionType} from './session'
-
-/**
- * A serialized session encoded as an Antelope struct, for transport through a
- * URL parameter or a message payload.
- */
-@Struct.type('url_encoded_session')
-export class URLEncodedSession extends Struct {
- @Struct.field(Checksum256) declare chain: Checksum256
- @Struct.field(Name) declare actor: Name
- @Struct.field(Name) declare permission: Name
- @Struct.field('string') declare walletPlugin: string
- @Struct.field('string', {optional: true}) declare data?: string
-
- static fromSession(data: SessionType): URLEncodedSession {
- const session = data instanceof Session ? data.serialize() : data
- return new URLEncodedSession({
- chain: session.chain,
- actor: session.actor,
- permission: session.permission,
- walletPlugin: JSON.stringify(session.walletPlugin),
- data: JSON.stringify(session.data),
- })
- }
-
- get serialized(): SerializedSession {
- return {
- chain: this.chain,
- actor: this.actor,
- permission: this.permission,
- walletPlugin: JSON.parse(this.walletPlugin),
- data: this.data ? JSON.parse(this.data) : undefined,
- }
- }
-}
diff --git a/packages/session/src/index-module.ts b/packages/session/src/index-module.ts
index 1767b85a..e256cfb0 100644
--- a/packages/session/src/index-module.ts
+++ b/packages/session/src/index-module.ts
@@ -1,4 +1,3 @@
-export * from './encoded'
export * from './kit'
export * from './login'
export * from './session'
diff --git a/packages/session/src/kit.ts b/packages/session/src/kit.ts
index c5f5a133..d89fa6bb 100644
--- a/packages/session/src/kit.ts
+++ b/packages/session/src/kit.ts
@@ -1,14 +1,12 @@
import {ChainDefinition, type ChainDefinitionType, type Fetch} from '@wharfkit/common'
import type {Contract} from '@wharfkit/contract'
import {
- Bytes,
Checksum256,
Checksum256Type,
Name,
NameType,
PermissionLevel,
PermissionLevelType,
- Serializer,
} from '@wharfkit/antelope'
import {
@@ -40,7 +38,6 @@ import {
import {SessionKeyManager} from './sessionkey/manager'
import {SessionKeyWalletPlugin} from './sessionkey/wallet'
import {SessionKeyConfig} from './sessionkey/types'
-import {URLEncodedSession} from './encoded'
export interface LoginOptions {
arbitrary?: Record // Arbitrary data that will be passed via context to wallet plugin
@@ -91,8 +88,6 @@ export interface SessionKitArgs {
export interface SessionKitOptions {
abis?: TransactABIDef[]
- acceptUrlSession?: boolean
- acceptUrlSessionParam?: string
allowModify?: boolean
contracts?: Contract[]
equalityFn?: SerializedSessionEqualityFn
@@ -113,8 +108,6 @@ export interface SessionKitOptions {
*/
export class SessionKit {
readonly abis: TransactABIDef[] = []
- readonly acceptUrlSession: boolean = false
- readonly acceptUrlSessionParam: string = 'incomingWharfSession'
readonly allowModify: boolean = true
readonly appName: string
readonly equalityFn: SerializedSessionEqualityFn = serializedSessionEquals
@@ -151,12 +144,6 @@ export class SessionKit {
if (options.abis) {
this.abis = [...options.abis]
}
- if (options.acceptUrlSession) {
- this.acceptUrlSession = options.acceptUrlSession
- }
- if (options.acceptUrlSessionParam) {
- this.acceptUrlSessionParam = options.acceptUrlSessionParam
- }
if (options.equalityFn) {
this.equalityFn = options.equalityFn
}
@@ -674,68 +661,14 @@ export class SessionKit {
}
/**
- * Read a session handed over through the current URL, if one is present.
- *
- * Requires `acceptUrlSession` and a browser environment. The parameter is
- * stripped from the URL once read, so a reload cannot replay it.
- */
- restoreFromURL(): SerializedSession | undefined {
- if (typeof window === 'undefined') {
- return
- }
- const url = new URL(window.location.href)
- const urlSessionParam = url.searchParams.get(this.acceptUrlSessionParam)
- if (urlSessionParam) {
- // Remove the session from the URL to prevent reuse, decodable or not
- url.searchParams.delete(this.acceptUrlSessionParam)
- window.history.replaceState(null, '', url)
- try {
- const encodedSession = Serializer.decode({
- data: Bytes.from(urlSessionParam, 'hex'),
- type: URLEncodedSession,
- })
- return encodedSession.serialized
- } catch {
- // eslint-disable-next-line no-console -- warn the developer since this may be unintentional
- console.warn('Failed to decode session from URL: ' + urlSessionParam)
- }
- }
- }
-
- private canRestore(serializedSession: SerializedSession): boolean {
- return (
- !!this.getWalletPlugin(serializedSession.walletPlugin.id) &&
- this.chains.some((c) => c.id.equals(serializedSession.chain))
- )
- }
-
- /**
- * Find the session to restore when the caller named none: the incoming URL
- * session if one is offered, otherwise the default session in storage.
+ * Find the session to restore when the caller named none: the default
+ * session in storage.
*/
private async restoreWithoutArgs(): Promise {
- let serializedSession: SerializedSession | undefined
-
- if (this.acceptUrlSession) {
- const fromURL = this.restoreFromURL()
- if (fromURL && this.canRestore(fromURL)) {
- serializedSession = fromURL
- } else if (fromURL) {
- // eslint-disable-next-line no-console -- warn the developer since this may be unintentional
- console.warn(
- `Ignoring session from URL for chain ${fromURL.chain} and wallet plugin '${fromURL.walletPlugin.id}', which this SessionKit does not support.`
- )
- }
- }
-
- if (!serializedSession) {
- const data = await this.storage.read('session')
- if (data) {
- serializedSession = JSON.parse(data)
- }
+ const data = await this.storage.read('session')
+ if (data) {
+ return JSON.parse(data)
}
-
- return serializedSession
}
/**
diff --git a/packages/session/src/session.ts b/packages/session/src/session.ts
index 3e3d4014..4ef82eb4 100644
--- a/packages/session/src/session.ts
+++ b/packages/session/src/session.ts
@@ -43,7 +43,6 @@ import {
TransactRevisions,
} from './transact'
import {SessionStorage} from './storage'
-import {URLEncodedSession} from './encoded'
import {
actionMatchesPermission,
buildSendTransaction2Options,
@@ -117,11 +116,6 @@ export interface PartialSerializedSession extends Partial {},
- }
- }
-
- // Mock window.location with a writable href property
- if (typeof (globalThis as any).window.location === 'undefined') {
- ;(globalThis as any).window.location = {href: ''}
- } else {
- try {
- ;(globalThis as any).window.location.href =
- (globalThis as any).window.location.href || ''
- } catch {
- ;(globalThis as any).window.location = {href: ''}
- }
- }
-
- // Ensure no sessions
- const sessions = await sessionKit.restoreAll()
- assert.lengthOf(sessions, 0)
-
- // Set the href to include an incomingWharfSession parameter
- window.location.href =
- 'https://somewhere.com?incomingWharfSession=73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d104208d9c1754de3000000000090b1ca737b226964223a2277616c6c65742d706c7567696e2d707269766174656b6579222c2264617461223a7b22707269766174654b6579223a225056545f4b315f32355850314c7431527438376879796d6f755369654262676e554541657253317951486939777148433255656b326d677a48227d7d010f7b226669656c64223a22666f6f227d'
-
- // Attempt to restore the session from the URL
- const session = await sessionKit.restore()
- if (!session) {
- throw new Error('Failed to restore session from URL')
- }
-
- // Ensure session is correct
- assert.isDefined(session)
- assert.isTrue(session.chain.id.equals(mockChainDefinition.id), 'Incorrect chain')
- assert.isTrue(session.actor.equals('wharfkit1111'), 'Incorrect actor')
- assert.isTrue(session.permission.equals('test'), 'Incorrect permission')
- assert.isTrue(
- session.walletPlugin instanceof WalletPluginPrivateKey,
- 'Incorrect walletPlugin type'
- )
- assert.equal(session.data.field, 'foo', 'Incorrect session data')
- assert.equal(session.walletPlugin.id, 'wallet-plugin-privatekey')
- assert.equal(
- session.walletPlugin.data.privateKey,
- 'PVT_K1_25XP1Lt1Rt87hyymouSieBbgnUEAerS1yQHi9wqHC2Uek2mgzH'
- )
-
- // Ensure session was persisted to storage
- const sessionsAfter = await sessionKit.restoreAll()
- assert.lengthOf(sessionsAfter, 1)
- })
- test('session from URL this kit cannot restore', async function () {
- const sessionKit = new SessionKit(mockSessionKitArgs, {
- ...mockSessionKitOptions,
- acceptUrlSession: true,
- storage: new MockStorage(),
- })
- const stored = makeSession('session1')
- await sessionKit.persistSession(stored)
-
- const existingWindow = (globalThis as any).window
- ;(globalThis as any).window = {
- history: {replaceState: () => undefined},
- location: {href: ''},
- }
- const craft = (walletPluginId: string, chain: Checksum256Type) =>
- Serializer.encode({
- object: URLEncodedSession.from({
- chain,
- actor: 'incoming1111',
- permission: 'active',
- walletPlugin: JSON.stringify({id: walletPluginId, data: {}}),
- }),
- }).toString('hex')
- const unsupported = [
- craft('wallet-plugin-unregistered', mockChainDefinition.id),
- craft(
- 'wallet-plugin-privatekey',
- '00000000000000000000000000000000000000000000000000000000deadbeef'
- ),
- ]
- try {
- for (const hex of unsupported) {
- ;(globalThis as any).window.location.href =
- `https://app.test?incomingWharfSession=${hex}`
- // Falls back to the stored session rather than throwing
- const restored = await sessionKit.restore()
- assert.isTrue(restored?.actor.equals('session1'))
- }
- } finally {
- ;(globalThis as any).window = existingWindow
- }
- })
- test('session from URL that fails to decode', async function () {
- const sessionKit = new SessionKit(mockSessionKitArgs, {
- ...mockSessionKitOptions,
- acceptUrlSession: true,
- storage: new MockStorage(),
- })
- const existingWindow = (globalThis as any).window
- let replaced: string | undefined
- ;(globalThis as any).window = {
- history: {
- replaceState: (_s: unknown, _t: unknown, url: URL) => {
- replaced = String(url)
- },
- },
- location: {href: 'https://app.test?incomingWharfSession=not-hex-at-all'},
- }
- try {
- assert.isUndefined(await sessionKit.restore())
- assert.equal(replaced, 'https://app.test/')
- } finally {
- ;(globalThis as any).window = existingWindow
- }
- })
- test('session from URL outside a browser', function () {
- const sessionKit = new SessionKit(mockSessionKitArgs, {
- ...mockSessionKitOptions,
- acceptUrlSession: true,
- storage: new MockStorage(),
- })
- const existing = (globalThis as any).window
- delete (globalThis as any).window
- try {
- assert.isUndefined(sessionKit.restoreFromURL())
- } finally {
- ;(globalThis as any).window = existing
- }
- })
test('no session returns undefined', async function () {
const sessionKit = new SessionKit(mockSessionKitArgs, {
...mockSessionKitOptions,
diff --git a/packages/session/test/tests/session.ts b/packages/session/test/tests/session.ts
index 8c304d45..1afd1667 100644
--- a/packages/session/test/tests/session.ts
+++ b/packages/session/test/tests/session.ts
@@ -1,20 +1,12 @@
import {assert} from 'chai'
-import SessionKit, {
- BaseTransactPlugin,
- ChainDefinition,
- Session,
- SessionOptions,
- URLEncodedSession,
-} from '$lib'
+import SessionKit, {BaseTransactPlugin, ChainDefinition, Session, SessionOptions} from '$lib'
import {
ABI,
ABIDef,
- Bytes,
FetchProvider,
Name,
PermissionLevel,
- Serializer,
Signature,
TimePointSec,
} from '@wharfkit/antelope'
@@ -45,7 +37,6 @@ const mockTransactOptions = {
suite('session', function () {
let session: Session
- let kit: SessionKit
setup(function () {
// Establish new session before each test
session = new Session(
@@ -553,75 +544,4 @@ suite('session', function () {
assert.equal(provider2.url, 'https://wax.greymass.com')
})
})
- suite('encoded', function () {
- setup(function () {
- // Establish new session kit before each test
- kit = new SessionKit(
- {
- appName: 'demo.app',
- chains: [
- {
- id: '73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d',
- url: 'https://jungle4.greymass.com',
- },
- ],
- ui: new MockUserInterface(),
- walletPlugins: [makeWallet()],
- },
- {
- fetch: mockFetch, // Required for unit tests
- storage: new MockStorage(),
- }
- )
- })
- test('serialized', async function () {
- const serialized = session.encode('serialized')
- const fromSerialized = await kit.restore(serialized)
- if (!fromSerialized) {
- throw new Error('Failed to restore session from serialized')
- }
- assert.equal(
- JSON.stringify(serialized),
- JSON.stringify(fromSerialized.encode('serialized'))
- )
- assert.deepEqual(serialized, fromSerialized.encode('serialized'))
- })
- test('json', async function () {
- const serialized = session.encode('serialized')
- const json = session.encode('json')
- const fromJson = await kit.restore(JSON.parse(json))
- if (!fromJson) {
- throw new Error('Failed to restore session from json')
- }
- assert.equal(JSON.stringify(serialized), JSON.stringify(fromJson.encode('serialized')))
- assert.deepEqual(serialized, fromJson.encode('serialized'))
- })
- test('encoded', async function () {
- const serialized = session.encode('serialized')
- const encoded = session.encode('encoded')
- const fromEncoded = await kit.restore(encoded.serialized)
- if (!fromEncoded) {
- throw new Error('Failed to restore session from encoded')
- }
- assert.equal(
- JSON.stringify(serialized),
- JSON.stringify(fromEncoded.encode('serialized'))
- )
- assert.deepEqual(serialized, fromEncoded.encode('serialized'))
- })
- test('url', async function () {
- const serialized = session.encode('serialized')
- const url = session.encode('url')
- const reconstructed = Serializer.decode({
- data: Bytes.from(url, 'hex'),
- type: URLEncodedSession,
- })
- const fromUrl = await kit.restore(reconstructed.serialized)
- if (!fromUrl) {
- throw new Error('Failed to restore session from url')
- }
- assert.equal(JSON.stringify(serialized), JSON.stringify(fromUrl.encode('serialized')))
- assert.deepEqual(serialized, fromUrl.encode('serialized'))
- })
- })
})
diff --git a/packages/signing-request/package.json b/packages/signing-request/package.json
index 52f60d98..4be7bfa2 100644
--- a/packages/signing-request/package.json
+++ b/packages/signing-request/package.json
@@ -1,6 +1,6 @@
{
"name": "@wharfkit/signing-request",
- "version": "4.0.0",
+ "version": "4.0.1",
"description": "Signing Request (ESR / EEP-7) encoder and decoder for Antelope blockchains",
"homepage": "https://wharfkit.com/docs/utilities/signing-request-library",
"license": "BSD-3-Clause",
diff --git a/packages/svelte-components/.gitignore b/packages/svelte-components/.gitignore
index 5280cd12..294b3857 100644
--- a/packages/svelte-components/.gitignore
+++ b/packages/svelte-components/.gitignore
@@ -22,6 +22,3 @@ Thumbs.db
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
-
-# tooling
-.opencode
diff --git a/packages/svelte-components/AGENTS.md b/packages/svelte-components/AGENTS.md
deleted file mode 100644
index 40f6f2f0..00000000
--- a/packages/svelte-components/AGENTS.md
+++ /dev/null
@@ -1,76 +0,0 @@
-## Agentic Coding Agents Guide
-
-This document provides guidelines for AI agents to effectively contribute to this repository.
-
-### Build and Test Commands
-
-- **Build**: `npm run build`
-- **Lint**: `npm run lint`
-- **Type Check**: `npm run check`
-- **Format**: `npm run format`
-
-There is no specific command for running a single test. Use `npm run check` to type-check the entire project.
-
-### Code Style
-
-- **Formatting**: Code is formatted with Prettier. Use tabs, single quotes, and a print width of 100.
-- **Imports**: Imports are organized by `prettier-plugin-organize-imports`.
-- **Types**: All code must be strictly typed, as defined in `tsconfig.json`.
-- **Naming Conventions**: Use camelCase for variables and functions, and PascalCase for Svelte components.
-- **Error Handling**: Use try/catch blocks for asynchronous operations and throw errors where appropriate.
-- **Components**: Create components in their own directories with an `index.ts` file to export them.
-- **Dependencies**: Use `pnpm` for package management.
-
-## Development Workflow: Spec → Code
-
-THESE INSTRUCTIONS ARE CRITICAL!
-
-They dramatically improve the quality of the work you create.
-
-### Phase 1: Requirements First
-
-When asked to implement any feature or make changes, ALWAYS start by asking:
-"Should I create a Spec for this task first?"
-
-IF user agrees:
-
-- Create a markdown file in `.opencode/scopes/FeatureName.md`
-- Interview the user to clarify:
-- Purpose & user problem
-- Success criteria
-- Scope & constraints
-- Technical considerations
-- Out of scope items
-
-### Phase 2: Review & Refine
-
-After drafting the Spec:
-
-- Present it to the user
-- Ask: "Does this capture your intent? Any changes needed?"
-- Iterate until user approves
-- End with: "Spec looks good? Type 'GO!' when ready to implement"
-
-### Phase 3: Implementation
-
-ONLY after user types "GO!" or explicitly approves:
-
-- Begin coding based on the Spec
-- Reference the Spec for decisions
-- Update Spec if scope changes, but ask user first.
-
-### File Organization
-
-\`\`\`
-
-.opencode/
-├── scopes/
-│ ├── FeatureName.md # Shared/committed Specs
-│ └── .local/ # Git-ignored experimental Specs
-│ └── Experiment.md
-
-\`\`\`
-
-**Remember: Think first, ask clarifying questions, _then_ code. The Spec is your north star.**
-
-(source: https://lukebechtel.com/blog/vibe-speccing)
diff --git a/packages/svelte-components/package.json b/packages/svelte-components/package.json
index 5f449650..d1b28703 100644
--- a/packages/svelte-components/package.json
+++ b/packages/svelte-components/package.json
@@ -1,6 +1,6 @@
{
"name": "@wharfkit/svelte-components",
- "version": "4.0.0",
+ "version": "4.0.1",
"description": "Svelte 5 and Tailwind v4 component library for Antelope applications",
"license": "BSD-3-Clause",
"repository": {
diff --git a/packages/token/package.json b/packages/token/package.json
index 50979c39..a935e90d 100644
--- a/packages/token/package.json
+++ b/packages/token/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/token",
"description": "Library to work with Antelope-blockchain system tokens.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-autocorrect/package.json b/packages/transact-plugin-autocorrect/package.json
index 66c9398e..59e54ffb 100644
--- a/packages/transact-plugin-autocorrect/package.json
+++ b/packages/transact-plugin-autocorrect/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-autocorrect",
"description": "A plugin to correct common issues users experience while performing transactions.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/transact-plugin-autocorrect",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-cosigner/package.json b/packages/transact-plugin-cosigner/package.json
index 307449e2..345e2611 100644
--- a/packages/transact-plugin-cosigner/package.json
+++ b/packages/transact-plugin-cosigner/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-cosigner",
"description": "Automatically cosign transactions to assume resource costs using a noop action.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/transact-plugin-cosigner",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-explorerlink/package.json b/packages/transact-plugin-explorerlink/package.json
index 4e1f31bf..074f18ab 100644
--- a/packages/transact-plugin-explorerlink/package.json
+++ b/packages/transact-plugin-explorerlink/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-explorerlink",
"description": "A transact plugin to display a link to a block explorer after a transaction is broadcast.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/transact-plugin-explorerlink",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-finality-callback/package.json b/packages/transact-plugin-finality-callback/package.json
index c98d1e71..086f6f97 100644
--- a/packages/transact-plugin-finality-callback/package.json
+++ b/packages/transact-plugin-finality-callback/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-finality-callback",
"description": "A template to create plugins for use with @wharfkit/session transact method.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/transact-plugin-finality-callback",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-finality-checker/package.json b/packages/transact-plugin-finality-checker/package.json
index 0be048ae..6242a9eb 100644
--- a/packages/transact-plugin-finality-checker/package.json
+++ b/packages/transact-plugin-finality-checker/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-finality-checker",
"description": "A template to create plugins for use with @wharfkit/session transact method.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/transact-plugin-finality-checker",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-mock/package.json b/packages/transact-plugin-mock/package.json
index 80259835..f6dfac63 100644
--- a/packages/transact-plugin-mock/package.json
+++ b/packages/transact-plugin-mock/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-mock",
"description": "A mock TransactPlugin to simulate specific events.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/transact-plugin-mock",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-msig-propose/package.json b/packages/transact-plugin-msig-propose/package.json
index 96510ea3..f4492c7f 100644
--- a/packages/transact-plugin-msig-propose/package.json
+++ b/packages/transact-plugin-msig-propose/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-msig-propose",
"description": "A template to create plugins for use with @wharfkit/session transact method.",
- "version": "4.0.0",
+ "version": "4.0.1",
"private": true,
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/transact-plugin-resource-provider/package.json b/packages/transact-plugin-resource-provider/package.json
index a96f5938..18bab4eb 100644
--- a/packages/transact-plugin-resource-provider/package.json
+++ b/packages/transact-plugin-resource-provider/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/transact-plugin-resource-provider",
"description": "Plugin to automatically provide network resources for transactions using the Resource Provider implementation standard.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/transact-plugin-resource-provider",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-anchor/package.json b/packages/wallet-plugin-anchor/package.json
index 4e5708bc..b0285fbd 100644
--- a/packages/wallet-plugin-anchor/package.json
+++ b/packages/wallet-plugin-anchor/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-anchor",
"description": "An Anchor plugin for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-anchor",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-cleos/package.json b/packages/wallet-plugin-cleos/package.json
index e4841e8f..b12ac738 100644
--- a/packages/wallet-plugin-cleos/package.json
+++ b/packages/wallet-plugin-cleos/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-cleos",
"description": "A template to create wallet plugins for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-cleos",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-cloudwallet/package.json b/packages/wallet-plugin-cloudwallet/package.json
index b9503ec8..ce4de17e 100644
--- a/packages/wallet-plugin-cloudwallet/package.json
+++ b/packages/wallet-plugin-cloudwallet/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-cloudwallet",
"description": "A WalletPlugin for My Cloud Wallet for use within the @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-cloudwallet",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-gatewallet/package.json b/packages/wallet-plugin-gatewallet/package.json
index bc435e13..b9bd9cbb 100644
--- a/packages/wallet-plugin-gatewallet/package.json
+++ b/packages/wallet-plugin-gatewallet/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-gatewallet",
"description": "A template to create wallet plugins for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-gatewallet",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-imtoken/package.json b/packages/wallet-plugin-imtoken/package.json
index 6baa5b27..4cfc8782 100644
--- a/packages/wallet-plugin-imtoken/package.json
+++ b/packages/wallet-plugin-imtoken/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-imtoken",
"description": "A template to create wallet plugins for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-imtoken",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-metamask/package.json b/packages/wallet-plugin-metamask/package.json
index 84e96311..b3458b23 100644
--- a/packages/wallet-plugin-metamask/package.json
+++ b/packages/wallet-plugin-metamask/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-metamask",
"description": "A MetaMask plugin for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-metamask",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-mimic/package.json b/packages/wallet-plugin-mimic/package.json
index 58905d96..227bfb9f 100644
--- a/packages/wallet-plugin-mimic/package.json
+++ b/packages/wallet-plugin-mimic/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-mimic",
"description": "A template to create wallet plugins for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"private": true,
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-mock/package.json b/packages/wallet-plugin-mock/package.json
index c51e2ab2..6f0d1972 100644
--- a/packages/wallet-plugin-mock/package.json
+++ b/packages/wallet-plugin-mock/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-mock",
"description": "A mock wallet for developers to use while building web applications.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-mock",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-paycash/package.json b/packages/wallet-plugin-paycash/package.json
index 1a0dc2ff..777e12ab 100644
--- a/packages/wallet-plugin-paycash/package.json
+++ b/packages/wallet-plugin-paycash/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-paycash",
"description": "A Wharf wallet plugin for the PayCash wallet",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-paycash",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-privatekey/package.json b/packages/wallet-plugin-privatekey/package.json
index b157b2de..0be3530c 100644
--- a/packages/wallet-plugin-privatekey/package.json
+++ b/packages/wallet-plugin-privatekey/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-privatekey",
"description": "A template to create wallet plugins for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-privatekey",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-scatter/package.json b/packages/wallet-plugin-scatter/package.json
index 4cd9f258..603188ce 100644
--- a/packages/wallet-plugin-scatter/package.json
+++ b/packages/wallet-plugin-scatter/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-scatter",
"description": "A WalletPlugin for the Scatter wallet for use within the @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-scatter",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-tacklebox/package.json b/packages/wallet-plugin-tacklebox/package.json
index 2dcb8c0a..c7b14f9d 100644
--- a/packages/wallet-plugin-tacklebox/package.json
+++ b/packages/wallet-plugin-tacklebox/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-tacklebox",
"description": "A TackleBox plugin for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-tacklebox",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-tokenpocket/package.json b/packages/wallet-plugin-tokenpocket/package.json
index f3523d06..eabe13f7 100644
--- a/packages/wallet-plugin-tokenpocket/package.json
+++ b/packages/wallet-plugin-tokenpocket/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-tokenpocket",
"description": "A WalletPlugin for the TokenPocket wallet for use within the @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-tokenpocket",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/wallet-plugin-web-authenticator/package.json b/packages/wallet-plugin-web-authenticator/package.json
index 7c2b98a5..1d0b9b4f 100644
--- a/packages/wallet-plugin-web-authenticator/package.json
+++ b/packages/wallet-plugin-web-authenticator/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/wallet-plugin-web-authenticator",
"description": "A Web Authenticator wallet plugin for use with @wharfkit/session.",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com/plugins/wallet-plugin-web-authenticator",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/web-renderer/package.json b/packages/web-renderer/package.json
index f283f401..ac1b6993 100644
--- a/packages/web-renderer/package.json
+++ b/packages/web-renderer/package.json
@@ -1,6 +1,6 @@
{
"name": "@wharfkit/web-renderer",
- "version": "4.0.0",
+ "version": "4.0.1",
"description": "",
"license": "BSD-3-Clause",
"engines": {
diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json
index 3d5f2f92..4cd63565 100644
--- a/packages/web-ui/package.json
+++ b/packages/web-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@wharfkit/web-ui",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"description": "Modern embedded UI renderer for WharfKit SessionKit",
"type": "module",
diff --git a/packages/webauthn/package.json b/packages/webauthn/package.json
index 38d29700..5e66dc39 100644
--- a/packages/webauthn/package.json
+++ b/packages/webauthn/package.json
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/webauthn",
"description": "WebAuthn helpers for antelope core",
- "version": "4.0.0",
+ "version": "4.0.1",
"homepage": "https://wharfkit.com",
"license": "BSD-3-Clause",
"engines": {
diff --git a/scripts/release.ts b/scripts/release.ts
index 384e7c54..45b3255c 100644
--- a/scripts/release.ts
+++ b/scripts/release.ts
@@ -456,10 +456,7 @@ function publish(force: boolean) {
if (semver.prerelease(version)) {
releaseArgs.push('--prerelease')
} else {
- releaseArgs.push(
- '--notes',
- `Release notes: https://wharfkit.com/docs/releases/${version.replace(/\./g, '-')}`
- )
+ releaseArgs.push('--notes', 'Release notes: https://wharfkit.com/docs/releases')
}
if (trySh('gh', ['release', 'view', tag]) !== null) {
log(`GitHub release for ${tag} already exists`)