mirror of
https://github.com/placeholder-soft/storytime.git
synced 2026-01-12 07:14:55 +08:00
feat: gasless mint
This commit is contained in:
@@ -79,7 +79,7 @@ module storytime::story_nft {
|
||||
}
|
||||
|
||||
// ===== Entrypoints =====
|
||||
|
||||
#[lint_allow(self_transfer)]
|
||||
public fun mint(name: String, image_url: String, ctx: &mut TxContext) {
|
||||
let sender = tx_context::sender(ctx);
|
||||
let id = object::new(ctx);
|
||||
@@ -101,6 +101,26 @@ module storytime::story_nft {
|
||||
transfer::public_transfer(nft, sender);
|
||||
}
|
||||
|
||||
public fun mint_to(to: address, name: String, image_url: String, ctx: &mut TxContext) {
|
||||
let id = object::new(ctx);
|
||||
let chapters = vector[];
|
||||
let nft = StoryNFT {
|
||||
id,
|
||||
name,
|
||||
image_url,
|
||||
chapters,
|
||||
auther: to
|
||||
};
|
||||
|
||||
event::emit(NFTMinted {
|
||||
object_id: object::id(&nft),
|
||||
creator: to,
|
||||
name: nft.name,
|
||||
});
|
||||
|
||||
transfer::public_transfer(nft, to);
|
||||
}
|
||||
|
||||
/// Transfer `nft` to `recipient`
|
||||
public fun transfer(
|
||||
nft: StoryNFT, recipient: address, _: &mut TxContext
|
||||
|
||||
@@ -28,24 +28,6 @@ async function main() {
|
||||
],
|
||||
});
|
||||
|
||||
// const [coin] = txb.splitCoins(txb.gas, [100]);
|
||||
|
||||
// transfer the split coin to a specific address
|
||||
// txb.transferObjects([coin], '0x380254779600ed29cb70c917255b084d12b5a760c4dadeceb8f7673d0fc99d1d');
|
||||
|
||||
// const bytes = await txb.build();
|
||||
// const serializedSignature = (await keypair.signTransactionBlock(bytes))
|
||||
// .signature;
|
||||
|
||||
// // verify the signature locally
|
||||
// const verified = await keypair
|
||||
// .getPublicKey()
|
||||
// .verifyTransactionBlock(bytes, serializedSignature)
|
||||
|
||||
// if (!verified) {
|
||||
// throw new Error('Signature verification failed');
|
||||
// }
|
||||
|
||||
// define sui client for the desired network.
|
||||
const client = new SuiClient({ url: getFullnodeUrl('devnet') });
|
||||
const result = await client.signAndExecuteTransactionBlock({
|
||||
|
||||
1
functions/.gitignore
vendored
1
functions/.gitignore
vendored
@@ -8,3 +8,4 @@ typings/
|
||||
# Node.js dependency directory
|
||||
node_modules/
|
||||
lib/
|
||||
src/credentials/admin-key.json
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"@google-cloud/functions-framework": "^3.3.0",
|
||||
"@mysten/bcs": "^0.9.0",
|
||||
"@mysten/sui.js": "^0.48.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"firebase-admin": "^11.11.1",
|
||||
"firebase-functions": "^4.3.1",
|
||||
@@ -30,4 +33,4 @@
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
87
functions/pnpm-lock.yaml
generated
87
functions/pnpm-lock.yaml
generated
@@ -8,9 +8,18 @@ dependencies:
|
||||
'@google-cloud/functions-framework':
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0
|
||||
'@mysten/bcs':
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.0
|
||||
'@mysten/sui.js':
|
||||
specifier: ^0.48.0
|
||||
version: 0.48.0
|
||||
'@types/express':
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
dotenv:
|
||||
specifier: ^16.3.1
|
||||
version: 16.3.1
|
||||
express:
|
||||
specifier: ^4.18.2
|
||||
version: 4.18.2
|
||||
@@ -801,6 +810,37 @@ packages:
|
||||
lodash: 4.17.21
|
||||
optional: true
|
||||
|
||||
/@mysten/bcs@0.9.0:
|
||||
resolution: {integrity: sha512-h56essa8oSS4/J0Dby8k8stMoOSt+QZIEIeZNtgTOWh9HeV69yFg2BUg/+Rk7jzfWzvUmw9lFyKNipXcD5QOTw==}
|
||||
dependencies:
|
||||
bs58: 5.0.0
|
||||
dev: false
|
||||
|
||||
/@mysten/sui.js@0.48.0:
|
||||
resolution: {integrity: sha512-kGeV5F3IYThiliVbIou3YOOGAr8TdNyub4hGY2gevnphFQF8SukwQk8cyzuEEsAVSDUt0rsA9xxpncSgX+RjMA==}
|
||||
engines: {node: '>=16'}
|
||||
dependencies:
|
||||
'@mysten/bcs': 0.9.0
|
||||
'@noble/curves': 1.3.0
|
||||
'@noble/hashes': 1.3.3
|
||||
'@scure/bip32': 1.3.3
|
||||
'@scure/bip39': 1.2.1
|
||||
'@suchipi/femver': 1.0.0
|
||||
superstruct: 1.0.3
|
||||
tweetnacl: 1.0.3
|
||||
dev: false
|
||||
|
||||
/@noble/curves@1.3.0:
|
||||
resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==}
|
||||
dependencies:
|
||||
'@noble/hashes': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@noble/hashes@1.3.3:
|
||||
resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==}
|
||||
engines: {node: '>= 16'}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/aspromise@1.1.2:
|
||||
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
|
||||
|
||||
@@ -834,6 +874,25 @@ packages:
|
||||
/@protobufjs/utf8@1.1.0:
|
||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||
|
||||
/@scure/base@1.1.4:
|
||||
resolution: {integrity: sha512-wznebWtt+ejH8el87yuD4i9xLSbYZXf1Pe4DY0o/zq/eg5I0VQVXVbFs6XIM0pNVCJ/uE3t5wI9kh90mdLUxtw==}
|
||||
dev: false
|
||||
|
||||
/@scure/bip32@1.3.3:
|
||||
resolution: {integrity: sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==}
|
||||
dependencies:
|
||||
'@noble/curves': 1.3.0
|
||||
'@noble/hashes': 1.3.3
|
||||
'@scure/base': 1.1.4
|
||||
dev: false
|
||||
|
||||
/@scure/bip39@1.2.1:
|
||||
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
|
||||
dependencies:
|
||||
'@noble/hashes': 1.3.3
|
||||
'@scure/base': 1.1.4
|
||||
dev: false
|
||||
|
||||
/@sinclair/typebox@0.27.8:
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
dev: true
|
||||
@@ -855,6 +914,10 @@ packages:
|
||||
'@sinonjs/commons': 3.0.0
|
||||
dev: true
|
||||
|
||||
/@suchipi/femver@1.0.0:
|
||||
resolution: {integrity: sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==}
|
||||
dev: false
|
||||
|
||||
/@szmarczak/http-timer@4.0.6:
|
||||
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1311,6 +1374,10 @@ packages:
|
||||
resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==}
|
||||
dev: false
|
||||
|
||||
/base-x@4.0.0:
|
||||
resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==}
|
||||
dev: false
|
||||
|
||||
/base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
requiresBuild: true
|
||||
@@ -1376,6 +1443,12 @@ packages:
|
||||
update-browserslist-db: 1.0.13(browserslist@4.22.2)
|
||||
dev: true
|
||||
|
||||
/bs58@5.0.0:
|
||||
resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==}
|
||||
dependencies:
|
||||
base-x: 4.0.0
|
||||
dev: false
|
||||
|
||||
/bser@2.1.1:
|
||||
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
|
||||
dependencies:
|
||||
@@ -1702,6 +1775,11 @@ packages:
|
||||
md5: 2.3.0
|
||||
dev: false
|
||||
|
||||
/dotenv@16.3.1:
|
||||
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/duplexify@4.1.2:
|
||||
resolution: {integrity: sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==}
|
||||
requiresBuild: true
|
||||
@@ -4011,6 +4089,11 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/superstruct@1.0.3:
|
||||
resolution: {integrity: sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
dev: false
|
||||
|
||||
/supports-color@5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -4099,6 +4182,10 @@ packages:
|
||||
/tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
|
||||
/tweetnacl@1.0.3:
|
||||
resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==}
|
||||
dev: false
|
||||
|
||||
/type-check@0.3.2:
|
||||
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "storytime-web3",
|
||||
"private_key_id": "c948532af7977cea6405e645825b2bdfd2c3e3d6",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDClnYzEADTVtoI\nKEt2gnpEpEKQLVOVWao7Z2QNalKtuEtZb3GqL5tFDWi5d0CzejoqADQTq30bIV/m\narYk5JnCDG8NlJ7prcJc2t5KfrKOX/ReVdy7Jn1pCy3wv7S5qChEYNOojzDufEmb\nBFclFUUGlFkZO5lgQHKQfXUr0j7khL0Z0d6QxhT8D4tEqLjCYz+ZRlPvHdEvP9kf\naBc3e/amQNXwwyhXidtOPwYsVkIBTmHBtUYXx4gcTJrOwumHwhJYQcRpgfjtt2yO\ncARYRlADtArJY+SXY6laSD9KNZfJ3CWKZtGcbtdMCOcRdMlD5hIOS7HH02Iy7wcv\nHuBckoW/AgMBAAECggEAGLTkUXZyNw3w2sFMV5dDMVej20zjWclhO8VUZv79fBuJ\ndv5snF21ZW6e7JGPha+la/N2U/BduaX4Aw3Ajm2iIjPUSTv6HhSBuFV9N6XfmQPm\nR1tijowEDLmD00IcDx337ZACaoVdLU1a8Sj7bEoc517K81EQbTAG+9aaG9QdnS5k\nD0HyHbcNneT6lQrjv+3iMh7zGwzmSWSxD81fKhrGalAB97OZwdiwZSQKp2icySRU\nsnhk0FFsggQ2KHaX26zrp0s9VSUGMvD82xDymSU6YWwrIlaC2P1lDy72bo2EaeRS\nh9qZKld7e1vFNUuKaZhiHPNF8CgEhq9ztVvOVgQW3QKBgQDmJqhDyoWSg7Qr1rba\nOs4UOigQnyRrd8vwarxyvm97tUTbzAeyaKvfxSNlxxRnGowh2Gm92sX7togJ+5w1\nnTBjxQjvvFLaTEsUdRQUPH7UcEbG71+YJpS82Izznc4UNYP3GLlsljQ20eQ+2PM/\nrHc4zm+qZb1MVoXiduCjKvU5SwKBgQDYcUh7J0TiKTDTWyyHagDVIImz0yfY3FXf\noqov8kJemApHgXSkH4NjSQV7bDpBWytt+kZEjIlLMXCx9/gUNRQIdAzX4n5qeOZr\nohRP6Fh7atycnlQQ+ifrjPcb+3OEuccxog+6nTqbTaJMig0Pa3yJqR1psIHlOxSF\nzeFzgSYw3QKBgQDRQmzT1pxEb0OuaHexTK57bbVoB4rRvAbN/f6CReyPim2le4m0\nCb/coh5hN/WxGU44p9DdMsG48GoYyZFqnhWVYV2SvKSIn73UR++NxsN63Os9jgBZ\nExtB6ZOfHeh2L4JhdTWDKb8n6QeirRfe2S09lVWqlP7dHf51vqjZMwHsqQKBgQC/\no9YETOm9wbDcgs6ze1UPYAbstJqEddqG73T2jO7c6Iu5clL/enOP9jinZlVSRtH2\nR3HuAe0SKc1ZlnAOHE0HixFQGNfLmA6U4GZRtiyZ4i1BcyKGAahU9HRbT1GiBAft\n10tL9SjOF3gLgvQ7YfVbweQsDz+D4sKyEm97IK/a9QKBgDNz3sj+qkbgMn0sW2Fw\n3o5h9oWvEk2Zvvpo8+g/w3xEgoS2RbboqsCAN+JNw/fKecFrlZA97xVgd6fQjZNb\nfnqPRx3uHFKQwaOGaEAc5yTA5JFDSBdeRO5qXrHnPTyqNbwk4hzIAz+LcSn0WJgW\neG0OLLjO1ZadmECODGEHlp8U\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "firebase-adminsdk-i9jvg@storytime-web3.iam.gserviceaccount.com",
|
||||
"client_id": "112791080690462614850",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-i9jvg%40storytime-web3.iam.gserviceaccount.com",
|
||||
"universe_domain": "googleapis.com"
|
||||
}
|
||||
58
functions/src/gaslessMint.ts
Normal file
58
functions/src/gaslessMint.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { fromHEX } from "@mysten/bcs";
|
||||
import { SuiClient, getFullnodeUrl } from "@mysten/sui.js/client";
|
||||
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
||||
import { TransactionBlock } from "@mysten/sui.js/transactions";
|
||||
import dotenv from "dotenv";
|
||||
import { CloudFunctionsTypeWithUid } from "./handlersType";
|
||||
dotenv.config();
|
||||
|
||||
export const gaslessMint: CloudFunctionsTypeWithUid["gaslessMint"] = async (
|
||||
to: string,
|
||||
title: string,
|
||||
imageUrl: string
|
||||
): Promise<{ objectId: string }> => {
|
||||
const key = process.env.SUI_PRIVATE_KEY;
|
||||
const keypair = Ed25519Keypair.fromSecretKey(fromHEX(key!), {
|
||||
skipValidation: false,
|
||||
});
|
||||
|
||||
const pk = keypair.getPublicKey();
|
||||
const sender = pk.toSuiAddress();
|
||||
|
||||
// create an example transaction block.
|
||||
const txb = new TransactionBlock();
|
||||
txb.setSender(sender);
|
||||
txb.setGasBudget(5_000_000);
|
||||
|
||||
txb.moveCall({
|
||||
target: `0x84abd49cedd1ddf02ab8e48c167df180b047cdd1e4c07d97434e382658dfffe5::story_nft::mint_to`,
|
||||
arguments: [txb.pure.address(to), txb.pure(title), txb.pure(imageUrl)],
|
||||
});
|
||||
|
||||
// define sui client for the desired network.
|
||||
const client = new SuiClient({ url: getFullnodeUrl("devnet") });
|
||||
const result = await client.signAndExecuteTransactionBlock({
|
||||
signer: keypair,
|
||||
transactionBlock: txb,
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
const transactionBlock = await client.waitForTransactionBlock({
|
||||
digest: result.digest,
|
||||
options: {
|
||||
showEvents: true,
|
||||
showEffects: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(transactionBlock);
|
||||
|
||||
console.log(`event: ${JSON.stringify(transactionBlock.events, null, 2)}`);
|
||||
|
||||
const objectId = (transactionBlock.events?.[0]?.parsedJson as any)
|
||||
.object_id as string;
|
||||
|
||||
return {
|
||||
objectId,
|
||||
};
|
||||
};
|
||||
@@ -15,6 +15,7 @@ import admin from "firebase-admin";
|
||||
import adminKey from "./credentials/admin-key.json";
|
||||
import { CloudFunctionsTypeWithUid } from "./handlersType";
|
||||
import { getStory } from "./getStory";
|
||||
import { gaslessMint } from "./gaslessMint";
|
||||
|
||||
export * from "./serve";
|
||||
|
||||
@@ -27,6 +28,7 @@ admin.initializeApp({
|
||||
const handlers: CloudFunctionsTypeWithUid = {
|
||||
generateImage,
|
||||
getStory,
|
||||
gaslessMint,
|
||||
};
|
||||
|
||||
export const execute = onCall(
|
||||
|
||||
6
model/functions.d.ts
vendored
6
model/functions.d.ts
vendored
@@ -3,4 +3,10 @@ export type CloudFunctionsType = {
|
||||
prompt: string
|
||||
): Promise<{ image_url: string; revised_prompt: string }>;
|
||||
getStory(id: string): Promise<{ id: string; story: string }>;
|
||||
|
||||
gaslessMint(
|
||||
to: string,
|
||||
title: string,
|
||||
imageUrl: string
|
||||
): Promise<{ objectId: string }>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user