From 22b15d058dbccc2dcc98ad3f0334916243b06465 Mon Sep 17 00:00:00 2001 From: nikos-terzo Date: Thu, 6 Jun 2024 19:16:50 +0300 Subject: [PATCH 1/7] Move contract one directory inside for creating room for future examples. --- move/{ => multisig}/Move.toml | 0 move/{ => multisig}/sources/multisig.move | 0 move/{ => multisig}/tests/multisig_tests.move | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename move/{ => multisig}/Move.toml (100%) rename move/{ => multisig}/sources/multisig.move (100%) rename move/{ => multisig}/tests/multisig_tests.move (100%) diff --git a/move/Move.toml b/move/multisig/Move.toml similarity index 100% rename from move/Move.toml rename to move/multisig/Move.toml diff --git a/move/sources/multisig.move b/move/multisig/sources/multisig.move similarity index 100% rename from move/sources/multisig.move rename to move/multisig/sources/multisig.move diff --git a/move/tests/multisig_tests.move b/move/multisig/tests/multisig_tests.move similarity index 100% rename from move/tests/multisig_tests.move rename to move/multisig/tests/multisig_tests.move From fbb63622cb6ea3aa9b5dca02ab0227a90d33a827 Mon Sep 17 00:00:00 2001 From: nikos-terzo Date: Thu, 6 Jun 2024 19:18:40 +0300 Subject: [PATCH 2/7] SharedSpace functionality. Multisig address can create the SharedSpace non-transferable object which can be used to authorize specific combination of multisig members. --- move/multisig/sources/shared_space.move | 89 +++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 move/multisig/sources/shared_space.move diff --git a/move/multisig/sources/shared_space.move b/move/multisig/sources/shared_space.move new file mode 100644 index 0000000..aabbac5 --- /dev/null +++ b/move/multisig/sources/shared_space.move @@ -0,0 +1,89 @@ +/// Module: shared_space +module multisig::shared_space { + use std::bcs; + + use sui::hash; + use sui::ed25519; + use sui::ecdsa_k1; + use sui::ecdsa_r1; + + use multisig::multisig; + + const ED25519_FLAG: u8 = 0; + const SECP256K1_FLAG: u8 = 1; + const SECP256R1_FLAG: u8 = 2; + + const EInvalidMultisigDerivation: u64 = 0; + const EInvalidSignature: u64 = 1; + const ELengthMismatch: u64 = 2; + const EPublicKeyFlagNotSupported: u64 = 3; + + public struct SharedSpace has key { + id: UID, + addr: address, + counter: u64, + pub_keys: vector>, + weights: vector, + threshold: u16, + n_members: u8 + } + + public struct MemberProof has drop { + indexes: vector + } + + public fun new_shared_space( + pub_keys: vector>, weights: vector, threshold: u16, ctx: &mut TxContext + ) { + let addr = ctx.sender(); + assert!(multisig::check_multisig_address_eq(pub_keys, weights, threshold, addr), EInvalidMultisigDerivation); + let n_members = pub_keys.length() as u8; + transfer::transfer( + SharedSpace { + id: object::new(ctx), + addr, + counter: 0, + pub_keys, + weights, + threshold, + n_members + }, + addr + ); + } + + public fun proove_membership(shared_space: &mut SharedSpace, indexes: vector, signatures: vector>): MemberProof { + + let (mut i, len) = (0, indexes.length()); + assert!(len == signatures.length(), ELengthMismatch); + let serialized = bcs::to_bytes(shared_space); + let msg = hash::blake2b256(&serialized); + + while (i < len) { + let index = indexes[i]; + // Explicitely copy the public key to not mess up the shared space + let mut pub_key = copy shared_space.pub_keys[index as u64]; + let flag = pub_key.remove(0); + if (flag == ED25519_FLAG) { + assert!(ed25519::ed25519_verify(&signatures[i], &pub_key, &msg), EInvalidSignature); + } else if (flag == SECP256K1_FLAG) { + assert!(ecdsa_k1::secp256k1_verify(&signatures[i], &pub_key, &msg, 0), EInvalidSignature); + } else if (flag == SECP256R1_FLAG) { + assert!(ecdsa_r1::secp256r1_verify(&signatures[i], &pub_key, &msg, 0), EInvalidSignature); + } else { + assert!(false, EPublicKeyFlagNotSupported); + }; + i = i + 1; + }; + + MemberProof { + indexes + } + } + + public use fun member_proof_indexes as MemberProof.indexes; + public fun member_proof_indexes(proof: &MemberProof): vector { + proof.indexes + } +} + From 83e09f1437d90b81c8beabf1f18edf3b22f7e785 Mon Sep 17 00:00:00 2001 From: nikos-terzo Date: Thu, 6 Jun 2024 19:32:01 +0300 Subject: [PATCH 3/7] Use multisig::shared_space to create only_admin_transferable_mintcap. --- .../.gitignore | 1 + .../only_admin_transferable_mintcap/Move.toml | 38 +++++++++ .../publish.sh | 83 +++++++++++++++++++ .../sources/example_mintcap.move | 80 ++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 move/only_admin_transferable_mintcap/.gitignore create mode 100644 move/only_admin_transferable_mintcap/Move.toml create mode 100755 move/only_admin_transferable_mintcap/publish.sh create mode 100644 move/only_admin_transferable_mintcap/sources/example_mintcap.move diff --git a/move/only_admin_transferable_mintcap/.gitignore b/move/only_admin_transferable_mintcap/.gitignore new file mode 100644 index 0000000..2046b85 --- /dev/null +++ b/move/only_admin_transferable_mintcap/.gitignore @@ -0,0 +1 @@ +.publish.res.json diff --git a/move/only_admin_transferable_mintcap/Move.toml b/move/only_admin_transferable_mintcap/Move.toml new file mode 100644 index 0000000..99a311e --- /dev/null +++ b/move/only_admin_transferable_mintcap/Move.toml @@ -0,0 +1,38 @@ +[package] +name = "only_admin_transferable_mintcap" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" } +multisig = { local = "../multisig" } + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +only_admin_transferable_mintcap = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" + diff --git a/move/only_admin_transferable_mintcap/publish.sh b/move/only_admin_transferable_mintcap/publish.sh new file mode 100755 index 0000000..da07720 --- /dev/null +++ b/move/only_admin_transferable_mintcap/publish.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# check dependencies are available. +for i in jq curl sui; do + if ! command -V ${i} 2>/dev/null; then + echo "${i} is not installed" + exit 1 + fi +done +cwd=$(pwd) +script_dir=$(dirname "$0") + +MOVE_PACKAGE_PATH=${cwd}/${script_dir}/ +GAS_BUDGET=900000000 # 0.9 SUI (Usually it is less than 0.5 SUI) + +network_alias=$(sui client active-env) +envs_json=$(sui client envs --json) +NETWORK=$(echo $envs_json | jq -r --arg alias $network_alias '.[0][] | select(.alias == $alias).rpc') +addresses=$(sui client addresses --json) + +echo "Checking if admin, buyer, and seller addresses are available" +has_admin=$(echo "$addresses" | jq -r '.addresses | map(contains(["admin"])) | any') +if [ "$has_admin" = "false" ]; then + echo "Did not find 'admin' in the addresses. Creating one." + sui client new-address ed25519 admin +fi +has_minter=$(echo "$addresses" | jq -r '.addresses | map(contains(["minter"])) | any') +if [ "$has_minter" = "false" ]; then + echo "Did not find 'minter' in the addresses. Creating one." + sui client new-address ed25519 minter +fi + +echo "Switching to admin address" +sui client switch --address admin + +echo "Checking if enough gas is available" +gas=$(sui client gas --json) +AVAILABLE=$(echo "$gas" | jq --argjson min_gas $GAS_BUDGET '.[] | select(.mistBalance > $min_gas).gasCoinId') +if [ -z "$AVAILABLE" ]; then + echo "Not enough gas to deploy contract, requesting from faucet for all addresses" + sui client faucet --address admin + sui client faucet --address minter + # If network_alias is localnet wait 2 sec + if [ "$network_alias" == "localnet" ]; then + sleep 2 + else + echo "Please try again after some time." + exit 1 + fi +fi + +echo "Publishing" +publish_res=$(sui client publish --skip-fetch-latest-git-deps --with-unpublished-dependencies --gas-budget ${GAS_BUDGET} --json ${MOVE_PACKAGE_PATH}) +echo ${publish_res} >.publish.res.json + +# Check if the command succeeded (exit status 0) +if [[ "$publish_res" =~ "error" ]]; then + # If yes, print the error message and exit the script + echo "Error during move contract publishing. Details : $publish_res" + exit 1 +fi + +# Parse PACKAGE_ID, PUBLISHER, MINT_CAP from the publish response +published_objs=$(echo "$publish_res" | jq -r '.objectChanges[] | select(.type == "published")') +PACKAGE_ID=$(echo "$published_objs" | jq -r '.packageId') + +new_objs=$(echo "$publish_res" | jq -r '.objectChanges[] | select(.type == "created")') +ADMIN_CAP=$(echo "$new_objs" | jq -r 'select(.objectType | contains("::mintcap::AdminCap")).objectId') + +suffix="" +if [ $# -eq 0 ]; then + suffix=".localnet" +fi + +cat >../../only_admin_transferable_mintcap/.env<<-_ENV +SUI_FULLNODE_URL=$NETWORK +PACKAGE_ID=$PACKAGE_ID +ADMIN_CAP=$ADMIN_CAP + +_ENV + +echo "Contract Deployment finished!" + diff --git a/move/only_admin_transferable_mintcap/sources/example_mintcap.move b/move/only_admin_transferable_mintcap/sources/example_mintcap.move new file mode 100644 index 0000000..5d26d84 --- /dev/null +++ b/move/only_admin_transferable_mintcap/sources/example_mintcap.move @@ -0,0 +1,80 @@ +/// Module: example_mintcap +/// This is an example on how to have an owned capability object +/// that the one who uses it is different than the one who can transfer it. +module only_admin_transferable_mintcap::mintcap { + + use multisig::multisig; + use multisig::shared_space::MemberProof; + + const ENotAuthorized: u64 = 0; + + public struct MintCap has key { + id: UID, + n_mints: u64 + } + + public struct AdminCap has key, store { + id: UID, + } + + public struct AdminAuth has drop {} + public struct MinterAuth has drop {} + + public struct EnEfTee has key, store { + id: UID + } + + fun init(ctx: &mut TxContext) { + transfer::public_transfer(AdminCap { id: object::new(ctx) }, ctx.sender()); + } + + public fun new_mint_cap(_: &AdminCap, n_mints: u64, admin_pk: vector, minter_pk: vector, ctx: &mut TxContext) { + let mut pks = vector::empty(); + // The order of the public keys pushed is important as we check below the index of the signature + pks.push_back(admin_pk); + pks.push_back(minter_pk); + let mut weights = vector::empty(); + weights.push_back(1); + weights.push_back(1); + let addr = multisig::derive_multisig_address_quiet(pks, weights, 1); + transfer::transfer(MintCap { id: object::new(ctx), n_mints }, addr); + } + + // Called from multisig address + // Uses MemberProof to create AdminAuth + public fun authorize_admin(member_proof: MemberProof): AdminAuth { + assert!(member_proof.indexes().contains(&0), ENotAuthorized); + AdminAuth {} + } + + public fun authorize_minter(member_proof: MemberProof): MinterAuth { + assert!(member_proof.indexes().contains(&1), ENotAuthorized); + MinterAuth {} + } + + /// Even if MintCap is owned by 1-out-of-2 Multisig address, only Admin can update n_mints + public fun update_n_mints(_: &AdminAuth, mint_cap: &mut MintCap, new_n_mints: u64) { + mint_cap.n_mints = new_n_mints; + } + + /// MintCap is owned by 1-out-of-2 Multisig address, but only Admin (1st pk) can transfer + /// Note that `new_admin_pk` or `new_minter_pk` can be the same as the old ones + public fun transfer(_: &AdminAuth, mint_cap: MintCap, new_admin_pk: vector, new_minter_pk: vector) { + let mut pks = vector::empty(); + // The order of the public keys pushed is important as we check below the index of the signature + pks.push_back(new_admin_pk); + pks.push_back(new_minter_pk); + let mut weights = vector::empty(); + weights.push_back(1); + weights.push_back(1); + let addr = multisig::derive_multisig_address_quiet(pks, weights, 1); + transfer::transfer(mint_cap, addr); + } + + /// MintCap is owned by 1-out-of-2 Multisig address, but only Minter (2nd pk) can mint + public fun mint(_: &MinterAuth, mint_cap: &mut MintCap, ctx: &mut TxContext): EnEfTee { + assert!(mint_cap.n_mints > 0, ENotAuthorized); + mint_cap.n_mints = mint_cap.n_mints - 1; + EnEfTee { id: object::new(ctx) } + } +} From 28e9803ee079bda5cef4e01cc2314c8bf2039f9c Mon Sep 17 00:00:00 2001 From: nikos-terzo Date: Wed, 3 Jul 2024 23:12:55 +0300 Subject: [PATCH 4/7] Working very basic minting --- only_admin_transferable_mintcap/.example.env | 3 + only_admin_transferable_mintcap/.gitignore | 1 + only_admin_transferable_mintcap/README.md | 12 + only_admin_transferable_mintcap/package.json | 24 + .../pnpm-lock.yaml | 563 ++++++++++++++++++ .../scripts/mint.ts | 36 ++ .../scripts/new-mintcap.ts | 20 + .../scripts/new-shared-space.ts | 22 + only_admin_transferable_mintcap/src/config.ts | 63 ++ .../src/create-mintcap.ts | 45 ++ .../src/create-shared-space.ts | 68 +++ .../src/get-mintcap.ts | 18 + .../src/get-shared-space.ts | 39 ++ only_admin_transferable_mintcap/src/mint.ts | 86 +++ .../src/multisig.ts | 46 ++ publish/publish.sh | 2 +- 16 files changed, 1047 insertions(+), 1 deletion(-) create mode 100644 only_admin_transferable_mintcap/.example.env create mode 100644 only_admin_transferable_mintcap/.gitignore create mode 100644 only_admin_transferable_mintcap/README.md create mode 100644 only_admin_transferable_mintcap/package.json create mode 100644 only_admin_transferable_mintcap/pnpm-lock.yaml create mode 100644 only_admin_transferable_mintcap/scripts/mint.ts create mode 100644 only_admin_transferable_mintcap/scripts/new-mintcap.ts create mode 100644 only_admin_transferable_mintcap/scripts/new-shared-space.ts create mode 100644 only_admin_transferable_mintcap/src/config.ts create mode 100644 only_admin_transferable_mintcap/src/create-mintcap.ts create mode 100644 only_admin_transferable_mintcap/src/create-shared-space.ts create mode 100644 only_admin_transferable_mintcap/src/get-mintcap.ts create mode 100644 only_admin_transferable_mintcap/src/get-shared-space.ts create mode 100644 only_admin_transferable_mintcap/src/mint.ts create mode 100644 only_admin_transferable_mintcap/src/multisig.ts diff --git a/only_admin_transferable_mintcap/.example.env b/only_admin_transferable_mintcap/.example.env new file mode 100644 index 0000000..9399571 --- /dev/null +++ b/only_admin_transferable_mintcap/.example.env @@ -0,0 +1,3 @@ +SUI_FULLNODE_URL=http://127.0.0.1:9000 +PACKAGE_ID=0xb9fe437d62fed5394a3203e52fc39e7b1a8a4403bc3e0f6df0f127d965d7ad62 +ADMIN_CAP=0x6768e3d26f90bf90d6dceb499fcf5e0e478d5759fbdb0433ec501d0d6e9a3693 diff --git a/only_admin_transferable_mintcap/.gitignore b/only_admin_transferable_mintcap/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/only_admin_transferable_mintcap/.gitignore @@ -0,0 +1 @@ +.env diff --git a/only_admin_transferable_mintcap/README.md b/only_admin_transferable_mintcap/README.md new file mode 100644 index 0000000..302acf6 --- /dev/null +++ b/only_admin_transferable_mintcap/README.md @@ -0,0 +1,12 @@ +# Shared Space + +Test out a new concept where we use multisig functionality to create a shared space where objects inside it are owned by multiple people. + +The objects owned by the multisig address are available from every public key(s) that can combine above threshold. + +But, we use multisig contract to check which public keys have signed and issue `MemberProof` to the sender of the transaction. + +Using this `MemberProof` we can authorize different combinations of public keys for different actions with these objects. + +In this example we have a `MintCap` that is transferrable and updatable only by the 1st member of the multisig (`AdminAuth`), +while usable for minting only by the 2nd member (`MintAuth`). diff --git a/only_admin_transferable_mintcap/package.json b/only_admin_transferable_mintcap/package.json new file mode 100644 index 0000000..f2f72da --- /dev/null +++ b/only_admin_transferable_mintcap/package.json @@ -0,0 +1,24 @@ +{ + "name": "only_admin_transferable_mintcap", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@mysten/bcs": "^0.11.1", + "@mysten/sui.js": "^0.54.1", + "blake2": "^5.0.0", + "dotenv": "^16.4.5", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@types/blake2": "^4.0.4", + "@types/lodash": "^4.17.4", + "@types/node": "^20.12.12" + } +} diff --git a/only_admin_transferable_mintcap/pnpm-lock.yaml b/only_admin_transferable_mintcap/pnpm-lock.yaml new file mode 100644 index 0000000..a20f900 --- /dev/null +++ b/only_admin_transferable_mintcap/pnpm-lock.yaml @@ -0,0 +1,563 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@mysten/bcs': + specifier: ^0.11.1 + version: 0.11.1 + '@mysten/sui.js': + specifier: ^0.54.1 + version: 0.54.1(svelte@4.2.17)(typescript@5.4.5) + blake2: + specifier: ^5.0.0 + version: 5.0.0 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + +devDependencies: + '@types/blake2': + specifier: ^4.0.4 + version: 4.0.4 + '@types/lodash': + specifier: ^4.17.4 + version: 4.17.4 + '@types/node': + specifier: ^20.12.12 + version: 20.12.12 + +packages: + + /@0no-co/graphql.web@1.0.7(graphql@16.8.1): + resolution: {integrity: sha512-E3Qku4mTzdrlwVWGPxklDnME5ANrEGetvYw4i2GCRlppWXXE4QD66j7pwb8HelZwS6LnqEChhrSOGCXpbiu6MQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + graphql: + optional: true + dependencies: + graphql: 16.8.1 + dev: false + + /@0no-co/graphqlsp@1.12.3(graphql@16.8.1)(typescript@5.4.5): + resolution: {integrity: sha512-U0hV/FmFgm2perV+mrXKe/7Z5F4/9rmOziHJYYQgKLwzUVuN5LQG0qs3cLBGxAqoosG0HfTi2cQkgMKY1CMbYQ==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + dependencies: + '@gql.tada/internal': 1.0.0(graphql@16.8.1)(typescript@5.4.5) + graphql: 16.8.1 + typescript: 5.4.5 + dev: false + + /@ampproject/remapping@2.3.0: + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + dev: false + + /@babel/helper-string-parser@7.24.1: + resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-validator-identifier@7.24.5: + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/parser@7.24.5: + resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.5 + dev: false + + /@babel/types@7.24.5: + resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.24.1 + '@babel/helper-validator-identifier': 7.24.5 + to-fast-properties: 2.0.0 + dev: false + + /@gql.tada/cli-utils@1.3.9(graphql@16.8.1)(svelte@4.2.17)(typescript@5.4.5): + resolution: {integrity: sha512-oRb7SG/+csx9CiypSJTI21KaLfulOUnhX1vxg4FXi2snub9XShkGR2XnnlJVTAOZXY9Vcxti1NutAElxdDkycA==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + dependencies: + '@0no-co/graphqlsp': 1.12.3(graphql@16.8.1)(typescript@5.4.5) + '@gql.tada/internal': 1.0.0(graphql@16.8.1)(typescript@5.4.5) + '@vue/compiler-dom': 3.4.27 + '@vue/language-core': 2.0.19(typescript@5.4.5) + graphql: 16.8.1 + svelte2tsx: 0.7.8(svelte@4.2.17)(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - svelte + dev: false + + /@gql.tada/internal@1.0.0(graphql@16.8.1)(typescript@5.4.5): + resolution: {integrity: sha512-B55aIYyZn5ewdgMqoJciPAwF5DKYX6HBabTU+ap/dpNH3EgJrLomc8Y8w+MCxCyOx+dXL9OduT6eWnVr7J7Eyg==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + dependencies: + '@0no-co/graphql.web': 1.0.7(graphql@16.8.1) + graphql: 16.8.1 + typescript: 5.4.5 + dev: false + + /@graphql-typed-document-node/core@3.2.0(graphql@16.8.1): + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 16.8.1 + dev: false + + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + dev: false + + /@jridgewell/resolve-uri@3.1.2: + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: false + + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /@mysten/bcs@0.11.1: + resolution: {integrity: sha512-xP85isNSYUCHd3O/g+TmZYmg4wK6cU8q/n/MebkIGP4CYVJZz2wU/G24xIZ3wI+0iTop4dfgA5kYrg/DQKCUzA==} + dependencies: + bs58: 5.0.0 + dev: false + + /@mysten/sui.js@0.54.1(svelte@4.2.17)(typescript@5.4.5): + resolution: {integrity: sha512-TSmGIX7U9O/uS9EKIQdv7/S69KTbBhMJVelXCafJE6IJw8iB9cN9uLu0+uklkBSDrbRmLSEY70jMr3uRFjReIg==} + engines: {node: '>=16'} + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) + '@mysten/bcs': 0.11.1 + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + '@suchipi/femver': 1.0.0 + bech32: 2.0.0 + gql.tada: 1.7.5(graphql@16.8.1)(svelte@4.2.17)(typescript@5.4.5) + graphql: 16.8.1 + superstruct: 1.0.4 + tweetnacl: 1.0.3 + transitivePeerDependencies: + - svelte + - typescript + dev: false + + /@noble/curves@1.4.0: + resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} + dependencies: + '@noble/hashes': 1.4.0 + dev: false + + /@noble/hashes@1.4.0: + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + dev: false + + /@scure/base@1.1.6: + resolution: {integrity: sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==} + dev: false + + /@scure/bip32@1.4.0: + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + dependencies: + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.6 + dev: false + + /@scure/bip39@1.3.0: + resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + dependencies: + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.6 + dev: false + + /@suchipi/femver@1.0.0: + resolution: {integrity: sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==} + dev: false + + /@types/blake2@4.0.4: + resolution: {integrity: sha512-r84TojGHMbBoH91XQjqoc1N89xy/LmcGb15k9OSdB2APb+xQfNcfbcFGMa9RbMmFsnIKCRDMpuKHBM04AwdgxQ==} + dependencies: + '@types/node': 20.12.12 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: false + + /@types/lodash@4.17.4: + resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==} + dev: true + + /@types/node@20.12.12: + resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@volar/language-core@2.2.4: + resolution: {integrity: sha512-7As47GndxGxsqqYnbreLrfB5NDUeQioPM2LJKUuB4/34c0NpEJ2byVl3c9KYdjIdiEstWZ9JLtLKNTaPWb5jtA==} + dependencies: + '@volar/source-map': 2.2.4 + dev: false + + /@volar/source-map@2.2.4: + resolution: {integrity: sha512-m92FLpR9vB1YEZfiZ+bfgpLrToL/DNkOrorWVep3pffHrwwI4Tx2oIQN+sqHJfKkiT5N3J1owC+8crhAEinfjg==} + dependencies: + muggle-string: 0.4.1 + dev: false + + /@vue/compiler-core@3.4.27: + resolution: {integrity: sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==} + dependencies: + '@babel/parser': 7.24.5 + '@vue/shared': 3.4.27 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + dev: false + + /@vue/compiler-dom@3.4.27: + resolution: {integrity: sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==} + dependencies: + '@vue/compiler-core': 3.4.27 + '@vue/shared': 3.4.27 + dev: false + + /@vue/language-core@2.0.19(typescript@5.4.5): + resolution: {integrity: sha512-A9EGOnvb51jOvnCYoRLnMP+CcoPlbZVxI9gZXE/y2GksRWM6j/PrLEIC++pnosWTN08tFpJgxhSS//E9v/Sg+Q==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/language-core': 2.2.4 + '@vue/compiler-dom': 3.4.27 + '@vue/shared': 3.4.27 + computeds: 0.0.1 + minimatch: 9.0.4 + path-browserify: 1.0.1 + typescript: 5.4.5 + vue-template-compiler: 2.7.16 + dev: false + + /@vue/shared@3.4.27: + resolution: {integrity: sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==} + dev: false + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: false + + /axobject-query@4.0.0: + resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} + dependencies: + dequal: 2.0.3 + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: false + + /base-x@4.0.0: + resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + dev: false + + /bech32@2.0.0: + resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} + dev: false + + /blake2@5.0.0: + resolution: {integrity: sha512-MLpq1DwBB9rC0IHuRc2gXLEAeNNTTYHEtvYCA5lK4RmoUPRmQLSLQrwgJvou62BvH9KP7whe8n+xxw45++fnYg==} + engines: {node: '>= 12.0.0'} + requiresBuild: true + dependencies: + nan: 2.20.0 + dev: false + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: false + + /bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + dependencies: + base-x: 4.0.0 + dev: false + + /code-red@1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + '@types/estree': 1.0.5 + acorn: 8.11.3 + estree-walker: 3.0.3 + periscopic: 3.1.0 + dev: false + + /computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + dev: false + + /css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.0 + dev: false + + /de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + dev: false + + /dedent-js@1.0.1: + resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} + dev: false + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + dev: false + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: false + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: false + + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /gql.tada@1.7.5(graphql@16.8.1)(svelte@4.2.17)(typescript@5.4.5): + resolution: {integrity: sha512-GepPTee+FWSVVZQ7GiJHzsGNo7gOb59kcn4mUPYLlkbpeJfOUwpuoB05ZNaXG0W4qZVPd1I7R2UgMHBjY1lGlQ==} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + dependencies: + '@0no-co/graphql.web': 1.0.7(graphql@16.8.1) + '@gql.tada/cli-utils': 1.3.9(graphql@16.8.1)(svelte@4.2.17)(typescript@5.4.5) + '@gql.tada/internal': 1.0.0(graphql@16.8.1)(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - graphql + - svelte + dev: false + + /graphql@16.8.1: + resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + dev: false + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: false + + /is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + dev: false + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + /lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.6.2 + dev: false + + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + dev: false + + /minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: false + + /muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + dev: false + + /nan@2.20.0: + resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + dev: false + + /no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.6.2 + dev: false + + /pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: false + + /periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + dependencies: + '@types/estree': 1.0.5 + estree-walker: 3.0.3 + is-reference: 3.0.2 + dev: false + + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: false + + /superstruct@1.0.4: + resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} + engines: {node: '>=14.0.0'} + dev: false + + /svelte2tsx@0.7.8(svelte@4.2.17)(typescript@5.4.5): + resolution: {integrity: sha512-ABK3RDFcy59AqAiU1N5Kxu1RnKrb1GDMrQjLgNgJfE8Q+coCKpjCAPtUVKQM2HnmuqeNWcT3NqfXbE+ZmN5Pow==} + peerDependencies: + svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0 + typescript: ^4.9.4 || ^5.0.0 + dependencies: + dedent-js: 1.0.1 + pascal-case: 3.1.2 + svelte: 4.2.17 + typescript: 5.4.5 + dev: false + + /svelte@4.2.17: + resolution: {integrity: sha512-N7m1YnoXtRf5wya5Gyx3TWuTddI4nAyayyIWFojiWV5IayDYNV5i2mRp/7qNGol4DtxEYxljmrbgp1HM6hUbmQ==} + engines: {node: '>=16'} + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + '@types/estree': 1.0.5 + acorn: 8.11.3 + aria-query: 5.3.0 + axobject-query: 4.0.0 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + locate-character: 3.0.0 + magic-string: 0.30.10 + periscopic: 3.1.0 + dev: false + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: false + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + dev: false + + /typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + dev: false + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + dev: false diff --git a/only_admin_transferable_mintcap/scripts/mint.ts b/only_admin_transferable_mintcap/scripts/mint.ts new file mode 100644 index 0000000..e47c7ed --- /dev/null +++ b/only_admin_transferable_mintcap/scripts/mint.ts @@ -0,0 +1,36 @@ +import { SuiClient } from "@mysten/sui.js/client"; +import { ADMIN_PUB_KEY, MINTER_KEYPAIR, MINTER_PUB_KEY, SUI_FULLNODE_URL } from "../src/config"; +import { mint } from "../src/mint"; +import { getMintCap } from "../src/get-mintcap"; +import { createMultisigPubKey } from "../src/multisig"; + +async function run() { + const client = new SuiClient({ url: SUI_FULLNODE_URL }); + + const multisigPubKey = await createMultisigPubKey({ + pubKeys: [ADMIN_PUB_KEY, MINTER_PUB_KEY], + weights: [1, 1], + threshold: 1, + }); + const mintCap = await getMintCap({ + client, + owner: multisigPubKey.toSuiAddress(), + }); + + if (!mintCap) { + throw new Error('MintCap not found'); + } + + const resp = await mint({ + client, + minterSigner: MINTER_KEYPAIR, + mintCap, + adminPubKey: ADMIN_PUB_KEY, + }); + console.log(resp); +} + +run(); + + +// async funct diff --git a/only_admin_transferable_mintcap/scripts/new-mintcap.ts b/only_admin_transferable_mintcap/scripts/new-mintcap.ts new file mode 100644 index 0000000..631c69d --- /dev/null +++ b/only_admin_transferable_mintcap/scripts/new-mintcap.ts @@ -0,0 +1,20 @@ +import { SuiClient } from "@mysten/sui.js/client"; +import { ADMIN_CAP, ADMIN_KEYPAIR, MINTER_PUB_KEY, PACKAGE_ID, SUI_FULLNODE_URL } from "../src/config"; +import { createMintCap } from "../src/create-mintcap"; + +async function run() { + const resp = await createMintCap({ + client: new SuiClient({ url: SUI_FULLNODE_URL }), + adminSigner: ADMIN_KEYPAIR, + adminCap: ADMIN_CAP, + minterPubKey: MINTER_PUB_KEY + }); + + const mintCap = resp.objectChanges.find((oc) => { + return oc.type === 'created' && oc.objectType === `${PACKAGE_ID}::mintcap::MintCap`; + }); + console.log(mintCap); +} + +run(); + diff --git a/only_admin_transferable_mintcap/scripts/new-shared-space.ts b/only_admin_transferable_mintcap/scripts/new-shared-space.ts new file mode 100644 index 0000000..e69ff91 --- /dev/null +++ b/only_admin_transferable_mintcap/scripts/new-shared-space.ts @@ -0,0 +1,22 @@ +import { SuiClient } from "@mysten/sui.js/client"; +import { ADMIN_CAP, ADMIN_KEYPAIR, MINTER_PUB_KEY, PACKAGE_ID, SUI_FULLNODE_URL } from "../src/config"; +import { createSharedSpace } from "../src/create-shared-space"; + +async function run() { + const resp = await createSharedSpace({ + client: new SuiClient({ url: SUI_FULLNODE_URL }), + signers: [ADMIN_KEYPAIR], + adminCap: ADMIN_CAP, + pubKeys: [ADMIN_KEYPAIR.getPublicKey(), MINTER_PUB_KEY], + weights: [1, 1], + threshold: 1 + }); + + const sharedSpace = resp.objectChanges.find((oc) => { + return oc.type === 'created' && oc.objectType === `${PACKAGE_ID}::shared_space::SharedSpace`; + }); + console.log(sharedSpace); +} + +run(); + diff --git a/only_admin_transferable_mintcap/src/config.ts b/only_admin_transferable_mintcap/src/config.ts new file mode 100644 index 0000000..c163628 --- /dev/null +++ b/only_admin_transferable_mintcap/src/config.ts @@ -0,0 +1,63 @@ +import 'dotenv/config'; +import { Ed25519Keypair } from '@mysten/sui.js/keypairs/ed25519'; +import { Secp256r1Keypair } from '@mysten/sui.js/keypairs/secp256r1'; +import { Secp256k1Keypair } from '@mysten/sui.js/keypairs/secp256k1'; +import { Keypair } from '@mysten/sui.js/cryptography'; +import { fromB64 } from '@mysten/bcs'; +import { execSync } from 'child_process'; +import { readFileSync } from 'fs'; + +// From .env +export const SUI_FULLNODE_URL = process.env.SUI_FULLNODE_URL!; +export const PACKAGE_ID = process.env.PACKAGE_ID!; +export const ADMIN_CAP = process.env.ADMIN_CAP!; + +// Parse admin, minter secret keys and create keypairs +function toKeypair(secretKey: Uint8Array): Keypair { + let signer: Keypair; + if (secretKey[0] === 0) { + signer = Ed25519Keypair.fromSecretKey( + secretKey + .slice(1) + ); + } else if (secretKey[0] === 1) { + signer = Secp256k1Keypair.fromSecretKey( + secretKey + .slice(1) + ); + } else if (secretKey[0] === 2) { + signer = Secp256r1Keypair.fromSecretKey( + secretKey + .slice(1) + ); + } else { + throw new Error(`Unknown key scheme: ${secretKey[0]}`); + } + return signer; +} + +const addresses = JSON.parse(execSync('sui client addresses --json').toString()) as { + activeAddress: string; + // list of 2 strings + addresses: string[][]; +}; + +// +const adminIndex = addresses.addresses.findIndex(([name, _]) => name === 'admin'); +const minterIndex = addresses.addresses.findIndex(([name, _]) => name === 'minter'); + +console.log("DANGER! Reading ~/.sui/sui_config/sui.keystore. Do not use this in production."); +const secretKeys = JSON.parse(readFileSync(`${process.env.HOME}/.sui/sui_config/sui.keystore`).toString()); + +export const ADMIN_KEYPAIR = toKeypair(fromB64(secretKeys[adminIndex])); +export const MINTER_KEYPAIR = toKeypair(fromB64(secretKeys[minterIndex])); +export const ADMIN_PUB_KEY = ADMIN_KEYPAIR.getPublicKey(); +export const MINTER_PUB_KEY = MINTER_KEYPAIR.getPublicKey(); + +if (ADMIN_KEYPAIR.toSuiAddress() != addresses.addresses[adminIndex][1]) { + throw new Error("Admin keypair does not match address"); +} +if (MINTER_KEYPAIR.toSuiAddress() != addresses.addresses[minterIndex][1]) { + throw new Error("Minter keypair does not match address"); +} + diff --git a/only_admin_transferable_mintcap/src/create-mintcap.ts b/only_admin_transferable_mintcap/src/create-mintcap.ts new file mode 100644 index 0000000..aa159c1 --- /dev/null +++ b/only_admin_transferable_mintcap/src/create-mintcap.ts @@ -0,0 +1,45 @@ +import { SuiClient, SuiObjectChange, SuiObjectRef, SuiTransactionBlockResponse, TransactionEffects } from '@mysten/sui.js/client'; +import { Keypair, PublicKey } from '@mysten/sui.js/cryptography'; +import { TransactionBlock } from '@mysten/sui.js/transactions'; +import { PACKAGE_ID } from './config'; + + // public fun new_mint_cap(_: &AdminCap, n_mints: u64, admin_pk: vector, minter_pk: vector, ctx: &mut TxContext) { +export async function createMintCap(args: { + client: SuiClient; + adminSigner: Keypair; + adminCap: string | SuiObjectRef; + minterPubKey: PublicKey; +}) { + const { client, adminSigner, adminCap, minterPubKey } = args; + + const txb = new TransactionBlock(); + const adminCapArg = + typeof adminCap === 'string' ? + txb.object(adminCap) : + txb.objectRef(adminCap); + + txb.moveCall({ + target: `${PACKAGE_ID}::mintcap::new_mint_cap`, + arguments: [ + adminCapArg, + txb.pure.u64(10), + txb.pure(Array.from(adminSigner.getPublicKey().toSuiBytes()), 'vector'), + txb.pure(Array.from(minterPubKey.toSuiBytes()), 'vector'), + ] + }); + + const resp = await client.signAndExecuteTransactionBlock({ + transactionBlock: txb, + signer: adminSigner, + options: { + showEffects: true, + showObjectChanges: true, + }, + requestType: 'WaitForLocalExecution' + }) as SuiTransactionBlockResponse & { + effects: TransactionEffects, + objectChanges: SuiObjectChange[] + }; + + return resp; +} diff --git a/only_admin_transferable_mintcap/src/create-shared-space.ts b/only_admin_transferable_mintcap/src/create-shared-space.ts new file mode 100644 index 0000000..8655911 --- /dev/null +++ b/only_admin_transferable_mintcap/src/create-shared-space.ts @@ -0,0 +1,68 @@ +import { SuiClient, SuiObjectChange, SuiObjectRef, SuiTransactionBlockResponse, TransactionEffects } from '@mysten/sui.js/client'; +import { Keypair, PublicKey } from '@mysten/sui.js/cryptography'; +import { TransactionBlock } from '@mysten/sui.js/transactions'; +import { bcs } from '@mysten/bcs'; +import { PACKAGE_ID } from './config'; +import { checkThreshold, createMultisigPubKey } from './multisig'; + +// Sponsor will be the first signer +export async function createSharedSpace(args: { + client: SuiClient; + signers: Keypair[]; + adminCap: string | SuiObjectRef; + pubKeys: PublicKey[]; + weights: number[]; + threshold: number; +}) { + const { client, signers, pubKeys, weights, threshold } = args; + + const multisigPubKey = await createMultisigPubKey({ + pubKeys, + weights, + threshold, + }); + if (!checkThreshold({multisigPubKey, signers})) { + throw new Error('Threshold not met'); + } + + const txb = new TransactionBlock(); + + const pubKeysRaw = bcs.vector(bcs.vector(bcs.u8())).serialize(pubKeys.map((pubKey) => Array.from(pubKey.toSuiBytes()))).toBytes(); + txb.moveCall({ + target: `${PACKAGE_ID}::shared_space::new_shared_space`, + arguments: [ + txb.pure(pubKeysRaw, 'vector>'), + txb.pure(weights, 'vector'), + txb.pure(threshold, 'u16'), + ] + }); + + txb.setSender(multisigPubKey.toSuiAddress()); + txb.setGasOwner(signers[0].toSuiAddress()); + + const txBytes = await txb.build({ + client, + // onlyTransactionKind / + }); + + const signatures = await Promise.all(signers.map(async (signer) => (await signer.signTransactionBlock(txBytes)).signature)); + const mutlisigSignature = multisigPubKey.combinePartialSignatures(signatures); + + const resp = await client.executeTransactionBlock({ + transactionBlock: txBytes, + signature: [mutlisigSignature, (await signers[0].signTransactionBlock(txBytes)).signature], + options: { + showEffects: true, + showObjectChanges: true, + }, + requestType: 'WaitForLocalExecution', + }) as SuiTransactionBlockResponse & { + effects: TransactionEffects, + objectChanges: SuiObjectChange[] + }; + + return resp; +} + + + diff --git a/only_admin_transferable_mintcap/src/get-mintcap.ts b/only_admin_transferable_mintcap/src/get-mintcap.ts new file mode 100644 index 0000000..752d298 --- /dev/null +++ b/only_admin_transferable_mintcap/src/get-mintcap.ts @@ -0,0 +1,18 @@ +import { SuiClient, SuiObjectData } from "@mysten/sui.js/client"; +import { PACKAGE_ID } from "./config"; + +export async function getMintCap(args: { + client: SuiClient; + owner: string; +}): Promise { + const { client, owner } = args; + + + const mintCap = await client.getOwnedObjects({ + owner, + filter: + { StructType: `${PACKAGE_ID}::mintcap::MintCap` }, + limit: 1, + }); + return mintCap.data.at(0)?.data; +} diff --git a/only_admin_transferable_mintcap/src/get-shared-space.ts b/only_admin_transferable_mintcap/src/get-shared-space.ts new file mode 100644 index 0000000..d78521d --- /dev/null +++ b/only_admin_transferable_mintcap/src/get-shared-space.ts @@ -0,0 +1,39 @@ +import { RawData, SuiClient } from "@mysten/sui.js/client"; +import { PACKAGE_ID } from "./config"; +import { SuiObjectRef } from "@mysten/sui.js/dist/cjs/bcs"; + +namespace getSharedSpaceBCS { + export type Args = { + client: SuiClient; + owner: string; + objectId?: string; + } + export type Result = Promise<{ + objectRef: SuiObjectRef, + rawData: Extract + }> +}; + +export async function getSharedSpaceBCS(args: getSharedSpaceBCS.Args): getSharedSpaceBCS.Result { + const { client, owner, objectId } = args; + + const sharedSpace = await client.getOwnedObjects({ + owner, + filter: objectId ? + { ObjectId: objectId } : + { StructType: `${PACKAGE_ID}::shared_space::SharedSpace` }, + limit: 1, + options: { + showBcs: true, + }, + }); + const bcs = sharedSpace.data[0].data?.bcs; + if (!bcs) { + throw new Error("Shared space not found"); + } + const rawData = bcs as Extract; + return { + objectRef: sharedSpace.data[0].data!, + rawData + } +} diff --git a/only_admin_transferable_mintcap/src/mint.ts b/only_admin_transferable_mintcap/src/mint.ts new file mode 100644 index 0000000..cecf0fa --- /dev/null +++ b/only_admin_transferable_mintcap/src/mint.ts @@ -0,0 +1,86 @@ +import { SuiClient, SuiObjectChange, SuiObjectRef, SuiTransactionBlockResponse, TransactionEffects } from '@mysten/sui.js/client'; +import { Keypair, PublicKey } from '@mysten/sui.js/cryptography'; +import { TransactionBlock } from '@mysten/sui.js/transactions'; +import { PACKAGE_ID } from './config'; +import { getSharedSpaceBCS } from './get-shared-space'; +import { bcs, fromB64 } from '@mysten/bcs'; +import { createMultisigPubKey } from './multisig'; +import blake2 from 'blake2'; + +export async function mint(args: { + client: SuiClient; + minterSigner: Keypair; + mintCap: SuiObjectRef; + adminPubKey: PublicKey; + +}) { + const { client, minterSigner, mintCap, adminPubKey } = args; + + const multisigPubKey = await createMultisigPubKey({ + pubKeys: [adminPubKey, minterSigner.getPublicKey()], + weights: [1, 1], + threshold: 1, + }); + + const sharedSpace = await getSharedSpaceBCS({ + client, + owner: multisigPubKey.toSuiAddress(), + }); + + let h = blake2.createHash('blake2b', { digestLength: 32 }); + h.update(Buffer.from(fromB64(sharedSpace.rawData.bcsBytes))); + const signature = minterSigner.signData(h.digest()); + const signatures = bcs.vector(bcs.vector(bcs.u8())).serialize([Array.from(signature)]).toBytes(); + const txb = new TransactionBlock(); + + const memberProof = txb.moveCall({ + target: `${PACKAGE_ID}::shared_space::proove_membership`, + arguments: [ + txb.objectRef(sharedSpace.objectRef), + txb.pure([1], 'vector'), + txb.pure(signatures, 'vector>'), + ] + }); + const mintAuth = txb.moveCall({ + target: `${PACKAGE_ID}::mintcap::authorize_minter`, + arguments: [ + memberProof + ] + }); + + const enEfTee = txb.moveCall({ + target: `${PACKAGE_ID}::mintcap::mint`, + arguments: [ + mintAuth, + txb.objectRef(mintCap), + ] + }); + + txb.transferObjects([enEfTee], minterSigner.toSuiAddress()); + + txb.setSender(multisigPubKey.toSuiAddress()); + txb.setGasOwner(minterSigner.toSuiAddress()); + + const txBytes = await txb.build({ + client, + // onlyTransactionKind / + }); + + const txSignature = (await minterSigner.signTransactionBlock(txBytes)).signature; + const mutlisigSignature = multisigPubKey.combinePartialSignatures([txSignature]); + + const resp = await client.executeTransactionBlock({ + transactionBlock: txBytes, + signature: [mutlisigSignature, txSignature], + options: { + showEffects: true, + showObjectChanges: true, + }, + requestType: 'WaitForLocalExecution' + }) as SuiTransactionBlockResponse & { + effects: TransactionEffects, + objectChanges: SuiObjectChange[] + }; + + return resp; +} diff --git a/only_admin_transferable_mintcap/src/multisig.ts b/only_admin_transferable_mintcap/src/multisig.ts new file mode 100644 index 0000000..d32ef35 --- /dev/null +++ b/only_admin_transferable_mintcap/src/multisig.ts @@ -0,0 +1,46 @@ +import { Keypair, PublicKey } from "@mysten/sui.js/cryptography"; +import { MultiSigPublicKey } from "@mysten/sui.js/multisig"; +import _ from 'lodash'; + +export async function createMultisigPubKey(args: { + pubKeys: PublicKey[]; + weights: number[]; + threshold: number; +}) { + const { pubKeys, weights, threshold } = args; + if (pubKeys.length !== weights.length || threshold === 0) { + throw new Error("pubKeys and weights must have the same length"); + } + + const publicKeys: { + publicKey: PublicKey; + weight: number; + }[] = _.zip(pubKeys, weights).map(([pubKey, weight]) => { + return { + publicKey: pubKey!, + weight: weight! + }; + }); + + const multisigSigner = MultiSigPublicKey.fromPublicKeys({ + threshold, + publicKeys, + }); + return multisigSigner; +} + +export function checkThreshold(args: { + multisigPubKey: MultiSigPublicKey, + signers: Keypair[] +}): boolean { + const { multisigPubKey, signers } = args; + let publicKeys = multisigPubKey.getPublicKeys(); + const sum = signers.reduce((acc, signer) => { + const pubKey = publicKeys.find((pubKeyWithWeight) => pubKeyWithWeight.publicKey.equals(signer.getPublicKey())); + if (!pubKey) { + throw new Error("Signer not included in pubKeys"); + } + return acc + pubKey.weight; + }, 0); + return sum >= multisigPubKey.getThreshold(); +} diff --git a/publish/publish.sh b/publish/publish.sh index 419f013..2782307 100755 --- a/publish/publish.sh +++ b/publish/publish.sh @@ -34,7 +34,7 @@ echo "Admin address used for publishing: ${ACTIVE_ADMIN_ADDRESS}" ACTIVE_NETWORK=$(sui client active-env) echo "Environment used is: ${ACTIVE_NETWORK}" -publish_res=$(sui client publish --gas-budget 2000000000 --json ../move/) +publish_res=$(sui client publish --gas-budget 2000000000 --json ../move/multisig) echo ${publish_res} >.publish.res.json From 008390a32cb34e5d9e8d217059d05a93336a7993 Mon Sep 17 00:00:00 2001 From: nikos-terzo Date: Wed, 3 Jul 2024 23:15:35 +0300 Subject: [PATCH 5/7] Move README.md to move package --- .../only_admin_transferable_mintcap}/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {only_admin_transferable_mintcap => move/only_admin_transferable_mintcap}/README.md (100%) diff --git a/only_admin_transferable_mintcap/README.md b/move/only_admin_transferable_mintcap/README.md similarity index 100% rename from only_admin_transferable_mintcap/README.md rename to move/only_admin_transferable_mintcap/README.md From 7a88f4acfd23e3abfd469cd3315cb985559f8ed1 Mon Sep 17 00:00:00 2001 From: nikos-terzo Date: Wed, 3 Jul 2024 23:47:33 +0300 Subject: [PATCH 6/7] Instructions to mint --- only_admin_transferable_mintcap/README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 only_admin_transferable_mintcap/README.md diff --git a/only_admin_transferable_mintcap/README.md b/only_admin_transferable_mintcap/README.md new file mode 100644 index 0000000..83d8240 --- /dev/null +++ b/only_admin_transferable_mintcap/README.md @@ -0,0 +1,6 @@ +## Instructions + +1. Run ../move/only_admin_transferable_mintcap/publish.sh to publish the package with its dependencies (multisig). publish.sh will also create an admin and a minter keypairs in your sui keystore. +2. Run ./scripts/new-shared-space.ts to create a `SharedSpace` object under the admin-minter multisig address. +3. Run ./scripts/new-mintcap.ts to create a `MintCap` object under the admin-minter multisig address. +4. Run ./scripts/mint.ts to mint an `EnEfTee` signing as minter under the multisig address. From 8f614556abaec2adb06eaf3903e4735f6745f42f Mon Sep 17 00:00:00 2001 From: nikos-terzo Date: Tue, 21 Jan 2025 09:29:23 +0200 Subject: [PATCH 7/7] BUG FIX: Forgot to increment shared_space.counter --- move/multisig/sources/shared_space.move | 3 +++ 1 file changed, 3 insertions(+) diff --git a/move/multisig/sources/shared_space.move b/move/multisig/sources/shared_space.move index aabbac5..a6176eb 100644 --- a/move/multisig/sources/shared_space.move +++ b/move/multisig/sources/shared_space.move @@ -52,12 +52,15 @@ module multisig::shared_space { ); } + /// Takes a SharedSpace object, along with the indexes and signatures of the public keys that have signed it. + /// Verifies the signatures and returns a MemberProof verifying the participants that have signed the shared space. public fun proove_membership(shared_space: &mut SharedSpace, indexes: vector, signatures: vector>): MemberProof { let (mut i, len) = (0, indexes.length()); assert!(len == signatures.length(), ELengthMismatch); let serialized = bcs::to_bytes(shared_space); let msg = hash::blake2b256(&serialized); + shared_space.counter = shared_space.counter + 1; while (i < len) { let index = indexes[i];