From 71f58f55d6069e57a5a227a0072179ee529c3f39 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:17:49 +0200 Subject: [PATCH 01/25] refactor fungible --- packages/core/stellar/src/add-pausable.ts | 5 +- packages/core/stellar/src/add-upgradeable.ts | 4 +- packages/core/stellar/src/fungible.ts | 80 ++++++++----------- .../core/stellar/src/set-access-control.ts | 32 ++++---- 4 files changed, 57 insertions(+), 64 deletions(-) diff --git a/packages/core/stellar/src/add-pausable.ts b/packages/core/stellar/src/add-pausable.ts index fb029a23e..1d9255762 100644 --- a/packages/core/stellar/src/add-pausable.ts +++ b/packages/core/stellar/src/add-pausable.ts @@ -7,6 +7,7 @@ import { defineFunctions } from './utils/define-functions'; export function addPausable(c: ContractBuilder, access: Access) { c.addUseClause('stellar_pausable', 'self', { alias: 'pausable' }); c.addUseClause('stellar_pausable', 'Pausable'); + c.addUseClause('stellar_default_impl_macro', 'default_impl'); const pausableTrait = { traitName: 'Pausable', @@ -19,8 +20,8 @@ export function addPausable(c: ContractBuilder, access: Access) { c.addTraitFunction(pausableTrait, functions.pause); c.addTraitFunction(pausableTrait, functions.unpause); - requireAccessControl(c, pausableTrait, functions.pause, access, 'caller'); - requireAccessControl(c, pausableTrait, functions.unpause, access, 'caller'); + requireAccessControl(c, pausableTrait, functions.pause, access); + requireAccessControl(c, pausableTrait, functions.unpause, access); } const functions = defineFunctions({ diff --git a/packages/core/stellar/src/add-upgradeable.ts b/packages/core/stellar/src/add-upgradeable.ts index eac50bd14..6c3071a6c 100644 --- a/packages/core/stellar/src/add-upgradeable.ts +++ b/packages/core/stellar/src/add-upgradeable.ts @@ -8,7 +8,7 @@ export function addUpgradeable(c: ContractBuilder, access: Access) { const functions = defineFunctions({ _require_auth: { args: [getSelfArg(), { name: 'operator', type: '&Address' }], - code: ['operator.require_auth();'], + code: [], }, }); @@ -26,5 +26,5 @@ export function addUpgradeable(c: ContractBuilder, access: Access) { c.addTraitFunction(upgradeableTrait, functions._require_auth); - requireAccessControl(c, upgradeableTrait, functions._require_auth, access, '*operator'); + requireAccessControl(c, upgradeableTrait, functions._require_auth, access, false); } diff --git a/packages/core/stellar/src/fungible.ts b/packages/core/stellar/src/fungible.ts index aa091b0f7..f05b8ca62 100644 --- a/packages/core/stellar/src/fungible.ts +++ b/packages/core/stellar/src/fungible.ts @@ -14,7 +14,7 @@ import { printContract } from './print'; import { toByteArray, toUint } from './utils/convert-strings'; export const defaults: Required = { - name: 'MyToken', + name: 'MyFungible', symbol: 'MTK', burnable: false, pausable: false, @@ -86,39 +86,34 @@ export function buildFungible(opts: FungibleOptions): Contract { function addBase(c: ContractBuilder, name: string, symbol: string, pausable: boolean) { // Set metadata - c.addConstructorCode( - `fungible::metadata::set_metadata(e, 18, String::from_str(e, "${name}"), String::from_str(e, "${symbol}"));`, - ); + c.addConstructorCode(`Base::set_metadata(e, 18, String::from_str(e, "${name}"), String::from_str(e, "${symbol}"));`); // Set token functions - c.addUseClause('stellar_fungible', 'self', { alias: 'fungible' }); + c.addUseClause('stellar_fungible', 'Base'); c.addUseClause('stellar_fungible', 'FungibleToken'); + c.addUseClause('stellar_default_impl_macro', 'default_impl'); c.addUseClause('soroban_sdk', 'contract'); c.addUseClause('soroban_sdk', 'contractimpl'); c.addUseClause('soroban_sdk', 'Address'); c.addUseClause('soroban_sdk', 'String'); c.addUseClause('soroban_sdk', 'Env'); - c.addUseClause('soroban_sdk', 'Symbol'); const fungibleTokenTrait = { traitName: 'FungibleToken', structName: c.name, - tags: ['contractimpl'], + tags: ['default_impl', 'contractimpl'], + assocType: 'type ContractType = Base;', }; - c.addTraitFunction(fungibleTokenTrait, functions.total_supply); - c.addTraitFunction(fungibleTokenTrait, functions.balance); - c.addTraitFunction(fungibleTokenTrait, functions.allowance); - c.addTraitFunction(fungibleTokenTrait, functions.transfer); - c.addTraitFunction(fungibleTokenTrait, functions.transfer_from); - c.addTraitFunction(fungibleTokenTrait, functions.approve); - c.addTraitFunction(fungibleTokenTrait, functions.decimals); - c.addTraitFunction(fungibleTokenTrait, functions.name); - c.addTraitFunction(fungibleTokenTrait, functions.symbol); + c.addTraitImplBlock(fungibleTokenTrait); if (pausable) { c.addUseClause('stellar_pausable_macros', 'when_not_paused'); + + c.addTraitFunction(fungibleTokenTrait, functions.transfer); c.addFunctionTag(functions.transfer, 'when_not_paused', fungibleTokenTrait); + + c.addTraitFunction(fungibleTokenTrait, functions.transfer_from); c.addFunctionTag(functions.transfer_from, 'when_not_paused', fungibleTokenTrait); } } @@ -134,13 +129,18 @@ function addBurnable(c: ContractBuilder, pausable: boolean) { section: 'Extensions', }; - c.addTraitFunction(fungibleBurnableTrait, functions.burn); - c.addTraitFunction(fungibleBurnableTrait, functions.burn_from); - if (pausable) { c.addUseClause('stellar_pausable_macros', 'when_not_paused'); + + c.addTraitFunction(fungibleBurnableTrait, functions.burn); c.addFunctionTag(functions.burn, 'when_not_paused', fungibleBurnableTrait); + + c.addTraitFunction(fungibleBurnableTrait, functions.burn_from); c.addFunctionTag(functions.burn_from, 'when_not_paused', fungibleBurnableTrait); + } else { + // prepend '#[default_impl]' + fungibleBurnableTrait.tags.unshift('default_impl'); + c.addTraitImplBlock(fungibleBurnableTrait); } } @@ -157,11 +157,10 @@ function addPremint(c: ContractBuilder, amount: string) { // TODO: handle signed int? const premintAbsolute = toUint(getInitialSupply(amount, 18), 'premint', 'u128'); - c.addUseClause('stellar_fungible', 'mintable::FungibleMintable'); c.addUseClause('soroban_sdk', 'Address'); c.addConstructorArgument({ name: 'recipient', type: 'Address' }); - c.addConstructorCode(`fungible::mintable::mint(e, &recipient, ${premintAbsolute});`); + c.addConstructorCode(`Base::mint(e, &recipient, ${premintAbsolute});`); } } @@ -207,21 +206,12 @@ export function getInitialSupply(premint: string, decimals: number): string { } function addMintable(c: ContractBuilder, access: Access, pausable: boolean) { - c.addUseClause('stellar_fungible', 'mintable::FungibleMintable'); - - const fungibleMintableTrait = { - traitName: 'FungibleMintable', - structName: c.name, - tags: ['contractimpl'], - section: 'Extensions', - }; - - c.addTraitFunction(fungibleMintableTrait, functions.mint); + c.addFreeFunction(functions.mint); - requireAccessControl(c, fungibleMintableTrait, functions.mint, access); + requireAccessControl(c, undefined, functions.mint, access); if (pausable) { - c.addFunctionTag(functions.mint, 'when_not_paused', fungibleMintableTrait); + c.addFunctionTag(functions.mint, 'when_not_paused'); } } @@ -230,17 +220,17 @@ const functions = defineFunctions({ total_supply: { args: [getSelfArg()], returns: 'i128', - code: ['fungible::total_supply(e)'], + code: ['Self::ContractType::total_supply(e)'], }, balance: { args: [getSelfArg(), { name: 'account', type: 'Address' }], returns: 'i128', - code: ['fungible::balance(e, &account)'], + code: ['Self::ContractType::balance(e, &account)'], }, allowance: { args: [getSelfArg(), { name: 'owner', type: 'Address' }, { name: 'spender', type: 'Address' }], returns: 'i128', - code: ['fungible::allowance(e, &owner, &spender)'], + code: ['Self::ContractType::allowance(e, &owner, &spender)'], }, transfer: { args: [ @@ -249,7 +239,7 @@ const functions = defineFunctions({ { name: 'to', type: 'Address' }, { name: 'amount', type: 'i128' }, ], - code: ['fungible::transfer(e, &from, &to, amount)'], + code: ['Self::ContractType::transfer(e, &from, &to, amount)'], }, transfer_from: { args: [ @@ -259,7 +249,7 @@ const functions = defineFunctions({ { name: 'to', type: 'Address' }, { name: 'amount', type: 'i128' }, ], - code: ['fungible::transfer_from(e, &spender, &from, &to, amount)'], + code: ['Self::ContractType::transfer_from(e, &spender, &from, &to, amount)'], }, approve: { args: [ @@ -269,28 +259,28 @@ const functions = defineFunctions({ { name: 'amount', type: 'i128' }, { name: 'live_until_ledger', type: 'u32' }, ], - code: ['fungible::approve(e, &owner, &spender, amount, live_until_ledger)'], + code: ['Self::ContractType::approve(e, &owner, &spender, amount, live_until_ledger)'], }, decimals: { args: [getSelfArg()], returns: 'u32', - code: ['fungible::metadata::decimals(e)'], + code: ['Self::ContractType::decimals(e)'], }, name: { args: [getSelfArg()], returns: 'String', - code: ['fungible::metadata::name(e)'], + code: ['Self::ContractType::name(e)'], }, symbol: { args: [getSelfArg()], returns: 'String', - code: ['fungible::metadata::symbol(e)'], + code: ['Self::ContractType::symbol(e)'], }, // Extensions burn: { args: [getSelfArg(), { name: 'from', type: 'Address' }, { name: 'amount', type: 'i128' }], - code: ['fungible::burnable::burn(e, &from, amount)'], + code: ['Self::ContractType::burn(e, &from, amount)'], }, burn_from: { args: [ @@ -299,10 +289,10 @@ const functions = defineFunctions({ { name: 'from', type: 'Address' }, { name: 'amount', type: 'i128' }, ], - code: ['fungible::burnable::burn_from(e, &spender, &from, amount)'], + code: ['Self::ContractType::burn_from(e, &spender, &from, amount)'], }, mint: { args: [getSelfArg(), { name: 'account', type: 'Address' }, { name: 'amount', type: 'i128' }], - code: ['fungible::mintable::mint(e, &account, amount);'], + code: ['Base::mint(e, &account, amount);'], }, }); diff --git a/packages/core/stellar/src/set-access-control.ts b/packages/core/stellar/src/set-access-control.ts index 0242b2dd9..d94ac798b 100644 --- a/packages/core/stellar/src/set-access-control.ts +++ b/packages/core/stellar/src/set-access-control.ts @@ -15,11 +15,8 @@ export function setAccessControl(c: ContractBuilder, access: Access): void { case 'ownable': { if (!c.ownable) { c.ownable = true; - c.addUseClause('soroban_sdk', 'symbol_short'); - c.addUseClause('soroban_sdk', 'Symbol'); - c.addVariable({ name: 'OWNER', type: 'Symbol', value: `symbol_short!("OWNER")` }); c.addConstructorArgument({ name: 'owner', type: 'Address' }); - c.addConstructorCode('e.storage().instance().set(&OWNER, &owner);'); + c.addConstructorCode('ownable::set_owner(e, &owner);'); } break; } @@ -40,7 +37,7 @@ export function requireAccessControl( trait: BaseTraitImplBlock | undefined, fn: BaseFunction, access: Access, - caller?: string, + useMacro: boolean = true, ): void { if (access === false) { access = DEFAULT_ACCESS_CONTROL; @@ -50,18 +47,23 @@ export function requireAccessControl( switch (access) { case 'ownable': { c.addUseClause('soroban_sdk', 'Address'); - const getOwner = 'let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");'; - if (caller) { - c.addUseClause('soroban_sdk', 'panic_with_error'); - c.addError('Unauthorized', 1); // TODO: Ensure there are no conflicts in error codes - c.addFunctionCodeBefore( - fn, - [getOwner, `if owner != ${caller} {`, ` panic_with_error!(e, ${c.name}Error::Unauthorized)`, '}'], - trait, - ); + c.addUseClause('stellar_ownable', 'self', { alias: 'ownable' }); + c.addUseClause('stellar_ownable', 'Ownable'); + c.addUseClause('stellar_ownable_macro', 'only_owner'); + + const ownableTrait = { + traitName: 'Ownable', + structName: c.name, + tags: ['default_impl', 'contractimpl'], + section: 'Utils', + }; + c.addTraitImplBlock(ownableTrait); + if (useMacro) { + c.addFunctionTag(fn, 'only_owner', trait); } else { - c.addFunctionCodeBefore(fn, [getOwner, 'owner.require_auth();'], trait); + c.addFunctionCodeBefore(fn, [`ownable::enforce_owner_auth(e);`], trait); } + break; } default: { From d68f897675985b761398e119ef479ea13a0d8d90 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:13:44 +0200 Subject: [PATCH 02/25] add stablecoin with limitations --- packages/core/stellar/src/api.ts | 8 ++ packages/core/stellar/src/build-generic.ts | 6 + packages/core/stellar/src/fungible.ts | 8 +- packages/core/stellar/src/generate/sources.ts | 7 ++ .../core/stellar/src/generate/stablecoin.ts | 22 ++++ packages/core/stellar/src/index.ts | 1 + packages/core/stellar/src/kind.ts | 1 + packages/core/stellar/src/stablecoin.ts | 111 ++++++++++++++++++ packages/ui/src/stellar/App.svelte | 10 +- .../ui/src/stellar/StablecoinControls.svelte | 99 ++++++++++++++++ 10 files changed, 268 insertions(+), 5 deletions(-) create mode 100644 packages/core/stellar/src/generate/stablecoin.ts create mode 100644 packages/core/stellar/src/stablecoin.ts create mode 100644 packages/ui/src/stellar/StablecoinControls.svelte diff --git a/packages/core/stellar/src/api.ts b/packages/core/stellar/src/api.ts index 329d24b35..7fbaa6f7c 100644 --- a/packages/core/stellar/src/api.ts +++ b/packages/core/stellar/src/api.ts @@ -1,6 +1,8 @@ import type { CommonContractOptions } from './common-options'; import type { FungibleOptions } from './fungible'; import type { NonFungibleOptions } from './non-fungible'; +import type { StablecoinOptions } from './stablecoin'; +import { printStablecoin, defaults as stablecoinDefaults } from './stablecoin'; import { printFungible, defaults as fungibledefaults } from './fungible'; import { printNonFungible, defaults as nonFungibledefaults } from './non-fungible'; @@ -26,6 +28,7 @@ export interface AccessControlAPI { export type Fungible = WizardContractAPI; // TODO add AccessControlAPI when access control is implemented, if useful export type NonFungible = WizardContractAPI; +export type Stablecoin = WizardContractAPI; export const fungible: Fungible = { print: printFungible, @@ -36,3 +39,8 @@ export const nonFungible: NonFungible = { print: printNonFungible, defaults: nonFungibledefaults, }; + +export const stablecoin: Stablecoin = { + print: printStablecoin, + defaults: stablecoinDefaults, +}; diff --git a/packages/core/stellar/src/build-generic.ts b/packages/core/stellar/src/build-generic.ts index d5fc9ca7e..2b4774d45 100644 --- a/packages/core/stellar/src/build-generic.ts +++ b/packages/core/stellar/src/build-generic.ts @@ -2,10 +2,13 @@ import type { FungibleOptions } from './fungible'; import { buildFungible } from './fungible'; import type { NonFungibleOptions } from './non-fungible'; import { buildNonFungible } from './non-fungible'; +import type { StablecoinOptions } from './stablecoin'; +import { buildStablecoin } from './stablecoin'; export interface KindedOptions { Fungible: { kind: 'Fungible' } & FungibleOptions; NonFungible: { kind: 'NonFungible' } & NonFungibleOptions; + Stablecoin: { kind: 'Stablecoin' } & StablecoinOptions; } export type GenericOptions = KindedOptions[keyof KindedOptions]; @@ -18,6 +21,9 @@ export function buildGeneric(opts: GenericOptions) { case 'NonFungible': return buildNonFungible(opts); + case 'Stablecoin': + return buildStablecoin(opts); + default: { const _: never = opts; throw new Error('Unknown kind'); diff --git a/packages/core/stellar/src/fungible.ts b/packages/core/stellar/src/fungible.ts index f05b8ca62..73b75114e 100644 --- a/packages/core/stellar/src/fungible.ts +++ b/packages/core/stellar/src/fungible.ts @@ -1,4 +1,4 @@ -import type { Contract } from './contract'; +import type { ContractBuilder } from './contract'; import { ContractBuilder } from './contract'; import type { Access } from './set-access-control'; import { requireAccessControl, setAccessControl } from './set-access-control'; @@ -39,7 +39,7 @@ export interface FungibleOptions extends CommonContractOptions { mintable?: boolean; } -function withDefaults(opts: FungibleOptions): Required { +export function withDefaults(opts: FungibleOptions): Required { return { ...opts, ...withCommonContractDefaults(opts), @@ -51,7 +51,7 @@ function withDefaults(opts: FungibleOptions): Required { }; } -export function buildFungible(opts: FungibleOptions): Contract { +export function buildFungible(opts: FungibleOptions): ContractBuilder { const c = new ContractBuilder(opts.name); const allOpts = withDefaults(opts); @@ -215,7 +215,7 @@ function addMintable(c: ContractBuilder, access: Access, pausable: boolean) { } } -const functions = defineFunctions({ +export const functions = defineFunctions({ // Token Functions total_supply: { args: [getSelfArg()], diff --git a/packages/core/stellar/src/generate/sources.ts b/packages/core/stellar/src/generate/sources.ts index ad6d33c95..5fc3953be 100644 --- a/packages/core/stellar/src/generate/sources.ts +++ b/packages/core/stellar/src/generate/sources.ts @@ -4,6 +4,7 @@ import crypto from 'crypto'; import { generateFungibleOptions } from './fungible'; import { generateNonFungibleOptions } from './non-fungible'; +import { generateStablecoinOptions } from './stablecoin'; import type { GenericOptions, KindedOptions } from '../build-generic'; import { buildGeneric } from '../build-generic'; import { printContract } from '../print'; @@ -24,6 +25,12 @@ export function* generateOptions(kind?: Kind): Generator { yield { kind: 'NonFungible', ...kindOpts }; } } + + if (!kind || kind === 'Stablecoin') { + for (const kindOpts of generateStablecoinOptions()) { + yield { kind: 'Stablecoin', ...kindOpts }; + } + } } interface GeneratedContract { diff --git a/packages/core/stellar/src/generate/stablecoin.ts b/packages/core/stellar/src/generate/stablecoin.ts new file mode 100644 index 000000000..461be1cd0 --- /dev/null +++ b/packages/core/stellar/src/generate/stablecoin.ts @@ -0,0 +1,22 @@ +import type { StablecoinOptions } from '../stablecoin'; +import { accessOptions } from '../set-access-control'; +import { infoOptions } from '../set-info'; +import { generateAlternatives } from './alternatives'; + +const booleans = [true, false]; + +const blueprint = { + name: ['MyStablecoin'], + symbol: ['MST'], + burnable: booleans, + pausable: booleans, + upgradeable: booleans, + mintable: booleans, + premint: ['1'], + access: accessOptions, + info: infoOptions, +}; + +export function* generateStablecoinOptions(): Generator> { + yield* generateAlternatives(blueprint); +} diff --git a/packages/core/stellar/src/index.ts b/packages/core/stellar/src/index.ts index 8677afee3..b0fa7fea3 100644 --- a/packages/core/stellar/src/index.ts +++ b/packages/core/stellar/src/index.ts @@ -23,3 +23,4 @@ export { contractsVersion, contractsVersionTag, compatibleContractsSemver } from export { fungible } from './api'; export { nonFungible } from './api'; +export { stablecoin } from './api'; diff --git a/packages/core/stellar/src/kind.ts b/packages/core/stellar/src/kind.ts index 7c3a62a7a..cba6637f4 100644 --- a/packages/core/stellar/src/kind.ts +++ b/packages/core/stellar/src/kind.ts @@ -16,6 +16,7 @@ function isKind(value: Kind | T): value is Kind { switch (value) { case 'Fungible': case 'NonFungible': + case 'Stablecoin': return true; default: { diff --git a/packages/core/stellar/src/stablecoin.ts b/packages/core/stellar/src/stablecoin.ts new file mode 100644 index 000000000..aa2b40f5e --- /dev/null +++ b/packages/core/stellar/src/stablecoin.ts @@ -0,0 +1,111 @@ +import type { Contract, ContractBuilder } from './contract'; +import { defineFunctions } from './utils/define-functions'; +import { getSelfArg } from './common-options'; +import { printContract } from './print'; +import type { FungibleOptions } from './fungible'; +import { + buildFungible, + defaults as fungibleDefaults, + withDefaults as withFungibleDefaults, + functions as fungibleFunctions, +} from './fungible'; +import { requireAccessControl, type Access } from './set-access-control'; + +export const defaults: Required = { + ...fungibleDefaults, + name: 'MyStablecoin', + symbol: 'MST', + limitations: false, +} as const; + +export function printStablecoin(opts: StablecoinOptions = defaults): string { + return printContract(buildStablecoin(opts)); +} + +export interface StablecoinOptions extends FungibleOptions { + limitations?: false | 'allowlist' | 'blocklist'; +} + +function withDefaults(opts: StablecoinOptions): Required { + return { + ...withFungibleDefaults(opts), + name: opts.name ?? defaults.name, + symbol: opts.symbol ?? defaults.symbol, + limitations: opts.limitations ?? defaults.limitations, + }; +} + +export function buildStablecoin(opts: StablecoinOptions): Contract { + const allOpts = withDefaults(opts); + + const c = buildFungible(allOpts); + + if (allOpts.limitations) { + addLimitations(c, allOpts.access, allOpts.limitations); + } + + return c; +} + +function addLimitations(c: ContractBuilder, access: Access, mode: boolean | 'allowlist' | 'blocklist') { + const type = mode === 'allowlist'; + + const limitationsTrait = { + traitName: type ? 'FungibleAllowList' : 'FungibleBlockList', + structName: c.name, + tags: ['contractimpl'], + section: 'Extensions', + }; + + if (type) { + c.addUseClause('stellar_fungible', 'allowlist::{AllowList, FungibleAllowList}'); + c.overrideAssocType('FungibleToken', 'type ContractType = AllowList;'); + } else { + c.addUseClause('stellar_fungible', 'blocklist::{BlockList, FungibleBlockList}'); + c.overrideAssocType('FungibleToken', 'type ContractType = BlockList;'); + } + + const [getterFn, addFn, removeFn] = type + ? [functions.allowed, functions.allow_user, functions.disallow_user] + : [functions.blocked, functions.block_user, functions.unblock_user]; + + c.addTraitFunction(limitationsTrait, getterFn); + + c.addTraitFunction(limitationsTrait, addFn); + requireAccessControl(c, limitationsTrait, addFn, access); + + c.addTraitFunction(limitationsTrait, removeFn); + requireAccessControl(c, limitationsTrait, removeFn, access); +} + +const functions = { + ...fungibleFunctions, + ...defineFunctions({ + allowed: { + args: [getSelfArg(), { name: 'account', type: 'Address' }], + returns: 'bool', + code: ['AllowList::allowed(e, &account)'], + }, + allow_user: { + args: [getSelfArg(), { name: 'user', type: 'Address' }, { name: 'operator', type: 'Address' }], + code: ['AllowList::allow_user(e, &user)'], + }, + disallow_user: { + args: [getSelfArg(), { name: 'user', type: 'Address' }, { name: 'operator', type: 'Address' }], + code: ['AllowList::disallow_user(e, &user)'], + }, + blocked: { + args: [getSelfArg(), { name: 'account', type: 'Address' }], + returns: 'bool', + code: ['BlockList::blocked(e, &account)'], + }, + block_user: { + args: [getSelfArg(), { name: 'user', type: 'Address' }, { name: 'operator', type: 'Address' }], + code: ['BlockList::block_user(e, &user)'], + }, + unblock_user: { + args: [getSelfArg(), { name: 'user', type: 'Address' }, { name: 'operator', type: 'Address' }], + code: ['BlockList::unblock_user(e, &user)'], + }, + }), +}; diff --git a/packages/ui/src/stellar/App.svelte b/packages/ui/src/stellar/App.svelte index 51c0ce1d0..3ee0c14fb 100644 --- a/packages/ui/src/stellar/App.svelte +++ b/packages/ui/src/stellar/App.svelte @@ -5,6 +5,7 @@ import FungibleControls from './FungibleControls.svelte'; import NonFungibleControls from './NonFungibleControls.svelte'; + import StablecoinControls from './StablecoinControls.svelte'; import CopyIcon from '../common/icons/CopyIcon.svelte'; import CheckIcon from '../common/icons/CheckIcon.svelte'; import DownloadIcon from '../common/icons/DownloadIcon.svelte'; @@ -47,7 +48,7 @@ const getButtonVisibilities = (opts?: KindedOptions[Kind]): ButtonVisibilities => { return { - downloadScaffold: opts?.kind === 'Fungible' || opts?.kind === 'NonFungible' ? true : false, + downloadScaffold: opts?.kind === 'Fungible' || opts?.kind === 'NonFungible' || opts?.kind === 'Stableocoin' ? true : false, }; }; @@ -93,6 +94,9 @@ case 'Fungible': opts.premint = initialOpts.premint ?? opts.premint; break; + case 'Stablecoin': + opts.premint = initialOpts.premint ?? opts.premint; + break; case 'NonFungible': break; } @@ -160,6 +164,7 @@ + @@ -213,6 +218,9 @@
+
+ +
diff --git a/packages/ui/src/stellar/StablecoinControls.svelte b/packages/ui/src/stellar/StablecoinControls.svelte
new file mode 100644
index 000000000..387ff2ad0
--- /dev/null
+++ b/packages/ui/src/stellar/StablecoinControls.svelte
@@ -0,0 +1,99 @@
+
+
+
+

Settings

+ +
+ + + +
+ + +
+ +
+

Features

+ +
+ + + + + + +
+
+ + + +
+ + +
+
+ + + From 07dbee825fc9efd473245f9c6931b0f7e173c92b Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:54:45 +0200 Subject: [PATCH 03/25] add access control --- packages/core/stellar/src/add-pausable.ts | 8 +- packages/core/stellar/src/add-upgradeable.ts | 6 +- packages/core/stellar/src/api.ts | 27 ++++-- packages/core/stellar/src/contract.ts | 13 ++- packages/core/stellar/src/fungible.ts | 49 ++++++++--- packages/core/stellar/src/non-fungible.ts | 4 + packages/core/stellar/src/print.ts | 10 +-- .../core/stellar/src/set-access-control.ts | 83 +++++++++++++++---- packages/core/stellar/src/stablecoin.ts | 14 +++- .../stellar/src/utils/define-functions.ts | 6 +- .../src/stellar/AccessControlSection.svelte | 52 ++++++++++++ .../ui/src/stellar/FungibleControls.svelte | 5 ++ .../ui/src/stellar/NonFungibleControls.svelte | 5 ++ .../ui/src/stellar/StablecoinControls.svelte | 5 ++ 14 files changed, 238 insertions(+), 49 deletions(-) create mode 100644 packages/ui/src/stellar/AccessControlSection.svelte diff --git a/packages/core/stellar/src/add-pausable.ts b/packages/core/stellar/src/add-pausable.ts index 1d9255762..547163f76 100644 --- a/packages/core/stellar/src/add-pausable.ts +++ b/packages/core/stellar/src/add-pausable.ts @@ -20,8 +20,12 @@ export function addPausable(c: ContractBuilder, access: Access) { c.addTraitFunction(pausableTrait, functions.pause); c.addTraitFunction(pausableTrait, functions.unpause); - requireAccessControl(c, pausableTrait, functions.pause, access); - requireAccessControl(c, pausableTrait, functions.unpause, access); + requireAccessControl(c, pausableTrait, functions.pause, access, { useMacro: true, role: 'pauser', caller: 'caller' }); + requireAccessControl(c, pausableTrait, functions.unpause, access, { + useMacro: true, + role: 'pauser', + caller: 'caller', + }); } const functions = defineFunctions({ diff --git a/packages/core/stellar/src/add-upgradeable.ts b/packages/core/stellar/src/add-upgradeable.ts index 6c3071a6c..9329adda4 100644 --- a/packages/core/stellar/src/add-upgradeable.ts +++ b/packages/core/stellar/src/add-upgradeable.ts @@ -26,5 +26,9 @@ export function addUpgradeable(c: ContractBuilder, access: Access) { c.addTraitFunction(upgradeableTrait, functions._require_auth); - requireAccessControl(c, upgradeableTrait, functions._require_auth, access, false); + requireAccessControl(c, upgradeableTrait, functions._require_auth, access, { + useMacro: false, + role: 'upgrader', + caller: 'operator', + }); } diff --git a/packages/core/stellar/src/api.ts b/packages/core/stellar/src/api.ts index 7fbaa6f7c..1ed40ab4d 100644 --- a/packages/core/stellar/src/api.ts +++ b/packages/core/stellar/src/api.ts @@ -2,9 +2,21 @@ import type { CommonContractOptions } from './common-options'; import type { FungibleOptions } from './fungible'; import type { NonFungibleOptions } from './non-fungible'; import type { StablecoinOptions } from './stablecoin'; -import { printStablecoin, defaults as stablecoinDefaults } from './stablecoin'; -import { printFungible, defaults as fungibledefaults } from './fungible'; -import { printNonFungible, defaults as nonFungibledefaults } from './non-fungible'; +import { + printStablecoin, + defaults as stablecoinDefaults, + isAccessControlRequired as stablecoinIsAccessControlRequired, +} from './stablecoin'; +import { + printFungible, + defaults as fungibledefaults, + isAccessControlRequired as fungibleIsAccessControlRequired, +} from './fungible'; +import { + printNonFungible, + defaults as nonFungibledefaults, + isAccessControlRequired as nonFungibleIsAccessControlRequired, +} from './non-fungible'; export interface WizardContractAPI { /** @@ -26,21 +38,24 @@ export interface AccessControlAPI { isAccessControlRequired: (opts: Partial) => boolean; } -export type Fungible = WizardContractAPI; // TODO add AccessControlAPI when access control is implemented, if useful -export type NonFungible = WizardContractAPI; -export type Stablecoin = WizardContractAPI; +export type Fungible = WizardContractAPI & AccessControlAPI; +export type NonFungible = WizardContractAPI & AccessControlAPI; +export type Stablecoin = WizardContractAPI & AccessControlAPI; export const fungible: Fungible = { print: printFungible, defaults: fungibledefaults, + isAccessControlRequired: fungibleIsAccessControlRequired, }; export const nonFungible: NonFungible = { print: printNonFungible, defaults: nonFungibledefaults, + isAccessControlRequired: nonFungibleIsAccessControlRequired, }; export const stablecoin: Stablecoin = { print: printStablecoin, defaults: stablecoinDefaults, + isAccessControlRequired: stablecoinIsAccessControlRequired, }; diff --git a/packages/core/stellar/src/contract.ts b/packages/core/stellar/src/contract.ts index 8a4d68b95..f6da7e091 100644 --- a/packages/core/stellar/src/contract.ts +++ b/packages/core/stellar/src/contract.ts @@ -46,16 +46,16 @@ export interface TraitImplBlock extends BaseTraitImplBlock { } export interface BaseFunction { - name: string; args: Argument[]; code: string[]; + name?: string; pub?: boolean; returns?: string; } export interface ContractFunction extends BaseFunction { + tags: string[]; codeBefore?: string[]; - tag?: string; } export interface Variable { @@ -172,6 +172,7 @@ export class ContractBuilder implements Contract { const contractFn: ContractFunction = { ...fn, pub: true, + tags: [], codeBefore: [], }; this.freeFunctionsMap.set(signature, contractFn); @@ -196,6 +197,7 @@ export class ContractBuilder implements Contract { // Otherwise, add the function const contractFn: ContractFunction = { ...fn, + tags: [], codeBefore: [], }; t.functions.push(contractFn); @@ -231,7 +233,7 @@ export class ContractBuilder implements Contract { addFunctionTag(fn: BaseFunction, tag: string, baseTrait?: BaseTraitImplBlock): void { const existingFn = this.getOrCreateFunction(fn, baseTrait); - existingFn.tag = tag; + existingFn.tags = [...(existingFn.tags ?? []), tag]; } addConstructorArgument(arg: Argument): void { @@ -244,6 +246,11 @@ export class ContractBuilder implements Contract { } addConstructorCode(code: string): void { + for (const existingCode of this.constructorCode) { + if (existingCode == code) { + return; + } + } this.constructorCode.push(code); } diff --git a/packages/core/stellar/src/fungible.ts b/packages/core/stellar/src/fungible.ts index 73b75114e..cc61c89df 100644 --- a/packages/core/stellar/src/fungible.ts +++ b/packages/core/stellar/src/fungible.ts @@ -1,4 +1,3 @@ -import type { ContractBuilder } from './contract'; import { ContractBuilder } from './contract'; import type { Access } from './set-access-control'; import { requireAccessControl, setAccessControl } from './set-access-control'; @@ -51,6 +50,10 @@ export function withDefaults(opts: FungibleOptions): Required { }; } +export function isAccessControlRequired(opts: Partial): boolean { + return opts.mintable === true || opts.pausable === true || opts.upgradeable === true; +} + export function buildFungible(opts: FungibleOptions): ContractBuilder { const c = new ContractBuilder(opts.name); @@ -118,6 +121,30 @@ function addBase(c: ContractBuilder, name: string, symbol: string, pausable: boo } } +function addMintable(c: ContractBuilder, access: Access, pausable: boolean) { + if (access === 'ownable') { + c.addFreeFunction(functions.mint); + + requireAccessControl(c, undefined, functions.mint, access); + + if (pausable) { + c.addFunctionTag(functions.mint, 'when_not_paused'); + } + } else if (access === 'roles') { + c.addFreeFunction(functions.mint_with_caller); + + requireAccessControl(c, undefined, functions.mint_with_caller, access, { + useMacro: true, + caller: 'caller', + role: 'minter', + }); + + if (pausable) { + c.addFunctionTag(functions.mint_with_caller, 'when_not_paused'); + } + } +} + function addBurnable(c: ContractBuilder, pausable: boolean) { c.addUseClause('stellar_fungible', 'burnable::FungibleBurnable'); c.addUseClause('soroban_sdk', 'Address'); @@ -205,16 +232,6 @@ export function getInitialSupply(premint: string, decimals: number): string { return result; } -function addMintable(c: ContractBuilder, access: Access, pausable: boolean) { - c.addFreeFunction(functions.mint); - - requireAccessControl(c, undefined, functions.mint, access); - - if (pausable) { - c.addFunctionTag(functions.mint, 'when_not_paused'); - } -} - export const functions = defineFunctions({ // Token Functions total_supply: { @@ -295,4 +312,14 @@ export const functions = defineFunctions({ args: [getSelfArg(), { name: 'account', type: 'Address' }, { name: 'amount', type: 'i128' }], code: ['Base::mint(e, &account, amount);'], }, + mint_with_caller: { + name: 'mint', + args: [ + getSelfArg(), + { name: 'account', type: 'Address' }, + { name: 'amount', type: 'i128' }, + { name: 'caller', type: 'Address' }, + ], + code: ['Base::mint(e, &account, amount);'], + }, }); diff --git a/packages/core/stellar/src/non-fungible.ts b/packages/core/stellar/src/non-fungible.ts index 9984a6f99..d606665ff 100644 --- a/packages/core/stellar/src/non-fungible.ts +++ b/packages/core/stellar/src/non-fungible.ts @@ -57,6 +57,10 @@ function withDefaults(opts: NonFungibleOptions): Required { }; } +export function isAccessControlRequired(opts: Partial): boolean { + return opts.mintable === true || opts.pausable === true || opts.upgradeable === true; +} + export function buildNonFungible(opts: NonFungibleOptions): Contract { const c = new ContractBuilder(opts.name); diff --git a/packages/core/stellar/src/print.ts b/packages/core/stellar/src/print.ts index 1099f8441..51740c7f9 100644 --- a/packages/core/stellar/src/print.ts +++ b/packages/core/stellar/src/print.ts @@ -246,7 +246,7 @@ function printFunction(fn: ContractFunction): Lines[] { } } - return printFunction2(fn.pub, head, args, fn.tag, fn.returns, undefined, codeLines); + return printFunction2(fn.pub, head, args, fn.tags, fn.returns, undefined, codeLines); } function printContractFunctions(contract: Contract): Lines[] { @@ -287,7 +287,7 @@ function printConstructor(contract: Contract): Lines[] { true, head, args.map(a => printArgument(a)), - undefined, + [], undefined, undefined, body, @@ -304,15 +304,15 @@ function printFunction2( pub: boolean | undefined, kindedName: string, args: string[], - tag: string | undefined, + tags: string[], returns: string | undefined, returnLine: string | undefined, code: Lines[], ): Lines[] { const fn = []; - if (tag !== undefined) { - fn.push(`#[${tag}]`); + for (let i = 0; i < tags.length; i++) { + fn.push(`#[${tags[i]}]`); } let accum = ''; diff --git a/packages/core/stellar/src/set-access-control.ts b/packages/core/stellar/src/set-access-control.ts index d94ac798b..9dd45848f 100644 --- a/packages/core/stellar/src/set-access-control.ts +++ b/packages/core/stellar/src/set-access-control.ts @@ -1,9 +1,16 @@ import type { BaseFunction, BaseTraitImplBlock, ContractBuilder } from './contract'; -export const accessOptions = [false, 'ownable'] as const; +export const accessOptions = [false, 'ownable', 'roles'] as const; export const DEFAULT_ACCESS_CONTROL = 'ownable'; export type Access = (typeof accessOptions)[number]; +export type OwnableProps = { useMacro: boolean }; +export type RolesProps = { useMacro: boolean; caller: string; role: string }; +export type AccessProps = { + useMacro: boolean; + caller?: string; + role?: string; +}; /** * Sets access control for the contract via constructor args. @@ -15,11 +22,40 @@ export function setAccessControl(c: ContractBuilder, access: Access): void { case 'ownable': { if (!c.ownable) { c.ownable = true; + c.addUseClause('soroban_sdk', 'Address'); + c.addUseClause('stellar_ownable', 'self', { alias: 'ownable' }); + c.addUseClause('stellar_ownable', 'Ownable'); + + const ownableTrait = { + traitName: 'Ownable', + structName: c.name, + tags: ['default_impl', 'contractimpl'], + section: 'Utils', + }; + c.addTraitImplBlock(ownableTrait); + c.addConstructorArgument({ name: 'owner', type: 'Address' }); c.addConstructorCode('ownable::set_owner(e, &owner);'); } break; } + case 'roles': { + c.addUseClause('soroban_sdk', 'Address'); + c.addUseClause('stellar_access_control', 'self', { alias: 'access_control' }); + c.addUseClause('stellar_access_control', 'AccessControl'); + + const accessControltrait = { + traitName: 'AccessControl', + structName: c.name, + tags: ['default_impl', 'contractimpl'], + section: 'Utils', + }; + c.addTraitImplBlock(accessControltrait); + + c.addConstructorArgument({ name: 'admin', type: 'Address' }); + c.addConstructorCode('access_control::set_admin(e, &admin);'); + break; + } default: { const _: never = access; throw new Error('Unknown value for `access`'); @@ -29,15 +65,13 @@ export function setAccessControl(c: ContractBuilder, access: Access): void { /** * Enables access control for the contract and restricts the given function with access control. - * - * If `caller` is provided, requires that the caller is the owner. Otherwise, requires that the owner is authorized. */ export function requireAccessControl( c: ContractBuilder, trait: BaseTraitImplBlock | undefined, fn: BaseFunction, access: Access, - useMacro: boolean = true, + accessProps: AccessProps = { useMacro: true }, ): void { if (access === false) { access = DEFAULT_ACCESS_CONTROL; @@ -46,19 +80,9 @@ export function requireAccessControl( switch (access) { case 'ownable': { - c.addUseClause('soroban_sdk', 'Address'); - c.addUseClause('stellar_ownable', 'self', { alias: 'ownable' }); - c.addUseClause('stellar_ownable', 'Ownable'); c.addUseClause('stellar_ownable_macro', 'only_owner'); - const ownableTrait = { - traitName: 'Ownable', - structName: c.name, - tags: ['default_impl', 'contractimpl'], - section: 'Utils', - }; - c.addTraitImplBlock(ownableTrait); - if (useMacro) { + if (accessProps.useMacro) { c.addFunctionTag(fn, 'only_owner', trait); } else { c.addFunctionCodeBefore(fn, [`ownable::enforce_owner_auth(e);`], trait); @@ -66,6 +90,35 @@ export function requireAccessControl( break; } + case 'roles': { + const { useMacro, caller, role } = accessProps; + + if (caller && role) { + c.addUseClause('soroban_sdk', 'Symbol'); + c.addConstructorArgument({ name: role, type: 'Address' }); + c.addConstructorCode(`access_control::grant_role_no_auth(e, &admin, &${role}, &Symbol::new(e, "${role}"));`); + + if (useMacro) { + c.addUseClause('stellar_access_control_macros', 'has_role'); + c.addFunctionTag(fn, `has_role(${caller}, "${role}")`, trait); + } else { + c.addFunctionCodeBefore( + fn, + [`access_control::ensure_role(e, ${caller}, &Symbol::new(e, "${role}"));`], + trait, + ); + } + } else { + if (useMacro) { + c.addUseClause('stellar_access_control_macros', 'only_admin'); + c.addFunctionTag(fn, 'only_admin', trait); + } else { + c.addFunctionCodeBefore(fn, ['access_control::enforce_admin_auth(e);'], trait); + } + } + + break; + } default: { const _: never = access; throw new Error('Unknown value for `access`'); diff --git a/packages/core/stellar/src/stablecoin.ts b/packages/core/stellar/src/stablecoin.ts index aa2b40f5e..f861dba1d 100644 --- a/packages/core/stellar/src/stablecoin.ts +++ b/packages/core/stellar/src/stablecoin.ts @@ -35,6 +35,10 @@ function withDefaults(opts: StablecoinOptions): Required { }; } +export function isAccessControlRequired(opts: Partial): boolean { + return opts.mintable === true || opts.limitations !== false || opts.pausable === true || opts.upgradeable === true; +} + export function buildStablecoin(opts: StablecoinOptions): Contract { const allOpts = withDefaults(opts); @@ -71,11 +75,17 @@ function addLimitations(c: ContractBuilder, access: Access, mode: boolean | 'all c.addTraitFunction(limitationsTrait, getterFn); + const accessProps = { + useMacro: true, + role: 'manager', + caller: 'operator', + }; + c.addTraitFunction(limitationsTrait, addFn); - requireAccessControl(c, limitationsTrait, addFn, access); + requireAccessControl(c, limitationsTrait, addFn, access, accessProps); c.addTraitFunction(limitationsTrait, removeFn); - requireAccessControl(c, limitationsTrait, removeFn, access); + requireAccessControl(c, limitationsTrait, removeFn, access, accessProps); } const functions = { diff --git a/packages/core/stellar/src/utils/define-functions.ts b/packages/core/stellar/src/utils/define-functions.ts index 3c89e6c76..3eb15e5df 100644 --- a/packages/core/stellar/src/utils/define-functions.ts +++ b/packages/core/stellar/src/utils/define-functions.ts @@ -1,9 +1,7 @@ import type { BaseFunction } from '../contract'; -type ImplicitNameFunction = Omit; +export function defineFunctions(fns: Record): Record; -export function defineFunctions(fns: Record): Record; - -export function defineFunctions(fns: Record): Record { +export function defineFunctions(fns: Record): Record { return Object.fromEntries(Object.entries(fns).map(([name, fn]) => [name, Object.assign({ name }, fn)])); } diff --git a/packages/ui/src/stellar/AccessControlSection.svelte b/packages/ui/src/stellar/AccessControlSection.svelte new file mode 100644 index 000000000..cf2ea4dad --- /dev/null +++ b/packages/ui/src/stellar/AccessControlSection.svelte @@ -0,0 +1,52 @@ + + + +
+ + +
+
diff --git a/packages/ui/src/stellar/FungibleControls.svelte b/packages/ui/src/stellar/FungibleControls.svelte index 317610de3..76cbe907e 100644 --- a/packages/ui/src/stellar/FungibleControls.svelte +++ b/packages/ui/src/stellar/FungibleControls.svelte @@ -4,6 +4,7 @@ import type { KindedOptions, OptionsErrorMessages } from '@openzeppelin/wizard-stellar'; import { premintPattern, fungible, infoDefaults } from '@openzeppelin/wizard-stellar'; + import AccessControlSection from './AccessControlSection.svelte'; import InfoSection from './InfoSection.svelte'; import { error } from '../common/error-tooltip'; @@ -15,6 +16,8 @@ }; export let errors: undefined | OptionsErrorMessages; + + $: requireAccessControl = fungible.isAccessControlRequired(opts);
@@ -73,4 +76,6 @@
+ + diff --git a/packages/ui/src/stellar/NonFungibleControls.svelte b/packages/ui/src/stellar/NonFungibleControls.svelte index bd731b0fb..40138c9e6 100644 --- a/packages/ui/src/stellar/NonFungibleControls.svelte +++ b/packages/ui/src/stellar/NonFungibleControls.svelte @@ -4,6 +4,7 @@ import type { KindedOptions, OptionsErrorMessages } from '@openzeppelin/wizard-stellar'; import { nonFungible, infoDefaults } from '@openzeppelin/wizard-stellar'; + import AccessControlSection from './AccessControlSection.svelte'; import InfoSection from './InfoSection.svelte'; import MintableSection from './MintableSection.svelte'; import { error } from '../common/error-tooltip'; @@ -16,6 +17,8 @@ export let errors: undefined | OptionsErrorMessages; + $: requireAccessControl = nonFungible.isAccessControlRequired(opts); + // Handler functions for checkbox changes function handleConsecutiveChange(value: boolean) { if (value) { @@ -96,4 +99,6 @@ }} /> + + diff --git a/packages/ui/src/stellar/StablecoinControls.svelte b/packages/ui/src/stellar/StablecoinControls.svelte index 387ff2ad0..90690ec01 100644 --- a/packages/ui/src/stellar/StablecoinControls.svelte +++ b/packages/ui/src/stellar/StablecoinControls.svelte @@ -4,6 +4,7 @@ import type { KindedOptions, OptionsErrorMessages } from '@openzeppelin/wizard-stellar'; import { premintPattern, stablecoin, infoDefaults } from '@openzeppelin/wizard-stellar'; + import AccessControlSection from './AccessControlSection.svelte'; import InfoSection from './InfoSection.svelte'; import ExpandableToggleRadio from '../common/ExpandableToggleRadio.svelte'; import { error } from '../common/error-tooltip'; @@ -16,6 +17,8 @@ }; export let errors: undefined | OptionsErrorMessages; + + $: requireAccessControl = stablecoin.isAccessControlRequired(opts);
@@ -95,5 +98,7 @@ + + From 05e63e4e75c5f1bf6ce8d4d72b5129810b33043b Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:27:59 +0200 Subject: [PATCH 04/25] non-fungible with default_impl and fixes --- packages/core/stellar/src/fungible.ts | 4 +- packages/core/stellar/src/non-fungible.ts | 171 ++++++++++-------- .../ui/src/stellar/StablecoinControls.svelte | 6 +- 3 files changed, 100 insertions(+), 81 deletions(-) diff --git a/packages/core/stellar/src/fungible.ts b/packages/core/stellar/src/fungible.ts index cc61c89df..3a549a776 100644 --- a/packages/core/stellar/src/fungible.ts +++ b/packages/core/stellar/src/fungible.ts @@ -297,7 +297,7 @@ export const functions = defineFunctions({ // Extensions burn: { args: [getSelfArg(), { name: 'from', type: 'Address' }, { name: 'amount', type: 'i128' }], - code: ['Self::ContractType::burn(e, &from, amount)'], + code: ['Base::burn(e, &from, amount)'], }, burn_from: { args: [ @@ -306,7 +306,7 @@ export const functions = defineFunctions({ { name: 'from', type: 'Address' }, { name: 'amount', type: 'i128' }, ], - code: ['Self::ContractType::burn_from(e, &spender, &from, amount)'], + code: ['Base::burn_from(e, &spender, &from, amount)'], }, mint: { args: [getSelfArg(), { name: 'account', type: 'Address' }, { name: 'amount', type: 'i128' }], diff --git a/packages/core/stellar/src/non-fungible.ts b/packages/core/stellar/src/non-fungible.ts index d606665ff..e14e4cd6d 100644 --- a/packages/core/stellar/src/non-fungible.ts +++ b/packages/core/stellar/src/non-fungible.ts @@ -58,7 +58,7 @@ function withDefaults(opts: NonFungibleOptions): Required { } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable === true || opts.pausable === true || opts.upgradeable === true; + return opts.mintable === true || opts.pausable === true || opts.upgradeable === true || opts.consecutive === true; } export function buildNonFungible(opts: NonFungibleOptions): Contract { @@ -121,13 +121,15 @@ export function buildNonFungible(opts: NonFungibleOptions): Contract { function addBase(c: ContractBuilder, name: string, symbol: string, pausable: boolean) { // Set metadata - c.addConstructorCode( - `Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "${name}"), String::from_str(e, "${symbol}"));`, - ); + c.addConstructorCode('let uri = String::from_str(e, "www.mytoken.com");'); + c.addConstructorCode(`let name = String::from_str(e, "${name}");`); + c.addConstructorCode(`let symbol = String::from_str(e, "${symbol}");`); + c.addConstructorCode(`Base::set_metadata(e, uri, name, symbol);`); // Set token functions c.addUseClause('stellar_non_fungible', 'Base'); c.addUseClause('stellar_non_fungible', 'NonFungibleToken'); + c.addUseClause('stellar_default_impl_macro', 'default_impl'); c.addUseClause('soroban_sdk', 'contract'); c.addUseClause('soroban_sdk', 'contractimpl'); c.addUseClause('soroban_sdk', 'Address'); @@ -137,28 +139,20 @@ function addBase(c: ContractBuilder, name: string, symbol: string, pausable: boo const nonFungibleTokenTrait = { traitName: 'NonFungibleToken', structName: c.name, - tags: ['contractimpl'], + tags: ['default_impl', 'contractimpl'], assocType: 'type ContractType = Base;', }; - // all the below may be eliminated by introducing `defaultimpl` macro at `tags` above, - // but we lose the customization for `pausable`, and so on... - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.owner_of); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.transfer); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.transfer_from); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.balance); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.approve); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.approve_for_all); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.get_approved); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.is_approved_for_all); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.name); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.symbol); - c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.token_uri); + c.addTraitImplBlock(nonFungibleTokenTrait); if (pausable) { c.addUseClause('stellar_pausable_macros', 'when_not_paused'); + + c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.transfer); c.addFunctionTag(baseFunctions.transfer, 'when_not_paused', nonFungibleTokenTrait); + c.addFunctionTag(baseFunctions.transfer_from, 'when_not_paused', nonFungibleTokenTrait); + c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.transfer_from); } } @@ -173,13 +167,18 @@ function addBurnable(c: ContractBuilder, pausable: boolean) { section: 'Extensions', }; - c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn); - c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn_from); - if (pausable) { c.addUseClause('stellar_pausable_macros', 'when_not_paused'); + + c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn); c.addFunctionTag(burnableFunctions.burn, 'when_not_paused', nonFungibleBurnableTrait); + + c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn_from); c.addFunctionTag(burnableFunctions.burn_from, 'when_not_paused', nonFungibleBurnableTrait); + } else { + // prepend '#[default_impl]' + nonFungibleBurnableTrait.tags.unshift('default_impl'); + c.addTraitImplBlock(nonFungibleBurnableTrait); } } @@ -197,18 +196,6 @@ function addEnumerable(c: ContractBuilder) { c.addTraitImplBlock(nonFungibleEnumerableTrait); c.overrideAssocType('NonFungibleToken', 'type ContractType = Enumerable;'); - - // Below is not required due to `defaultimpl` macro. If we require to customize the functions, - // then we should: - // 1. get rid of the `defaultimpl` macro form `tags` above, - // 2. get rid of `c.addImplementedTrait(nonFungibleEnumerableTrait);` line, - // 3. uncomment the section below: - // 4. uncomment `get_owner_token_id` and `get_token_id` from `enumerableFunctions` definition - /* - c.addFunction(nonFungibleEnumerableTrait, enumerableFunctions.total_supply); - c.addFunction(nonFungibleEnumerableTrait, enumerableFunctions.get_owner_token_id); - c.addFunction(nonFungibleEnumerableTrait, enumerableFunctions.get_token_id); - */ } function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) { @@ -226,48 +213,48 @@ function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) { c.overrideAssocType('NonFungibleToken', 'type ContractType = Consecutive;'); - c.addFreeFunction(consecutiveFunctions.batch_mint); + const mintFn = access === 'ownable' ? consecutiveFunctions.batch_mint : consecutiveFunctions.batch_mint_with_caller; + c.addFreeFunction(mintFn); if (pausable) { - c.addFunctionTag(consecutiveFunctions.batch_mint, 'when_not_paused'); + c.addFunctionTag(mintFn, 'when_not_paused'); } - requireAccessControl(c, undefined, consecutiveFunctions.batch_mint, access); + requireAccessControl(c, undefined, mintFn, access, { + useMacro: true, + role: 'minter', + caller: 'caller', + }); } function addMintable(c: ContractBuilder, enumerable: boolean, pausable: boolean, sequential: boolean, access: Access) { - if (!enumerable) { + const accessProps = { useMacro: true, role: 'minter', caller: 'caller' }; + + let mintFn; + + if (enumerable) { if (sequential) { - c.addUseClause('stellar_non_fungible', 'Base::sequential_mint'); - c.addFreeFunction(baseFunctions.sequential_mint); - requireAccessControl(c, undefined, baseFunctions.sequential_mint, access); - if (pausable) { - c.addFunctionTag(baseFunctions.sequential_mint, 'when_not_paused'); - } + mintFn = + access === 'ownable' ? enumerableFunctions.sequential_mint : enumerableFunctions.sequential_mint_with_caller; } else { - c.addUseClause('stellar_non_fungible', 'Base::mint'); - c.addFreeFunction(baseFunctions.mint); - requireAccessControl(c, undefined, baseFunctions.mint, access); - if (pausable) { - c.addFunctionTag(baseFunctions.mint, 'when_not_paused'); - } + mintFn = + access === 'ownable' + ? enumerableFunctions.non_sequential_mint + : enumerableFunctions.non_sequential_mint_with_caller; } - } - - if (enumerable) { + } else { if (sequential) { - c.addFreeFunction(enumerableFunctions.sequential_mint); - requireAccessControl(c, undefined, enumerableFunctions.sequential_mint, access); - if (pausable) { - c.addFunctionTag(enumerableFunctions.sequential_mint, 'when_not_paused'); - } + mintFn = access === 'ownable' ? baseFunctions.sequential_mint : baseFunctions.sequential_mint_with_caller; } else { - c.addFreeFunction(enumerableFunctions.non_sequential_mint); - requireAccessControl(c, undefined, enumerableFunctions.non_sequential_mint, access); - if (pausable) { - c.addFunctionTag(enumerableFunctions.non_sequential_mint, 'when_not_paused'); - } + mintFn = access === 'ownable' ? baseFunctions.mint : baseFunctions.mint_with_caller; } } + + c.addFreeFunction(mintFn); + requireAccessControl(c, undefined, mintFn, access, accessProps); + + if (pausable) { + c.addFunctionTag(mintFn, 'when_not_paused'); + } } const baseFunctions = defineFunctions({ @@ -351,10 +338,26 @@ const baseFunctions = defineFunctions({ args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }], code: ['Base::mint(e, &to, token_id);'], }, + mint_with_caller: { + name: 'mint', + args: [ + getSelfArg(), + { name: 'to', type: 'Address' }, + { name: 'token_id', type: 'u32' }, + { name: 'caller', type: 'Address' }, + ], + code: ['Base::mint(e, &to, token_id);'], + }, sequential_mint: { + name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }], code: ['Base::sequential_mint(e, &to);'], }, + sequential_mint_with_caller: { + name: 'mint', + args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'caller', type: 'Address' }], + code: ['Base::sequential_mint(e, &to);'], + }, }); const burnableFunctions = defineFunctions({ @@ -379,33 +382,49 @@ const enumerableFunctions = defineFunctions({ returns: 'u32', code: ['non_fungible::enumerable::Enumerable::total_supply(e)'], }, - // These are not currently used, see addEnumerable() above - /* - get_owner_token_id: { - args: [getSelfArg(), { name: 'owner', type: 'Address' }, { name: 'index', type: 'u32' }], - returns: 'u32', - code: ['Enumerable::get_owner_token_id(e, &owner, index)'], - }, - get_token_id: { - args: [getSelfArg(), { name: 'index', type: 'u32' }], - returns: 'u32', - code: ['Enumerable::get_token_id(e, index)'], - }, - */ non_sequential_mint: { + name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }], - code: ['Enumerable::non_sequential_mint(e, &to, token_id);'], // TODO: unify `mint` name in Stellar-Contracts across extensions + code: ['Enumerable::non_sequential_mint(e, &to, token_id);'], + }, + non_sequential_mint_with_caller: { + name: 'mint', + args: [ + getSelfArg(), + { name: 'to', type: 'Address' }, + { name: 'token_id', type: 'u32' }, + { name: 'caller', type: 'Address' }, + ], + code: ['Enumerable::non_sequential_mint(e, &to, token_id);'], }, sequential_mint: { + name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }], code: ['Enumerable::sequential_mint(e, &to);'], }, + sequential_mint_with_caller: { + name: 'mint', + args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'caller', type: 'Address' }], + code: ['Enumerable::sequential_mint(e, &to);'], + }, }); const consecutiveFunctions = defineFunctions({ batch_mint: { + name: 'batch_mint', args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'amount', type: 'u32' }], returns: 'u32', code: ['Consecutive::batch_mint(e, &to, amount);'], }, + batch_mint_with_caller: { + name: 'batch_mint', + args: [ + getSelfArg(), + { name: 'to', type: 'Address' }, + { name: 'amount', type: 'u32' }, + { name: 'caller', type: 'Address' }, + ], + returns: 'u32', + code: ['Consecutive::batch_mint(e, &to, amount);'], + }, }); diff --git a/packages/ui/src/stellar/StablecoinControls.svelte b/packages/ui/src/stellar/StablecoinControls.svelte index 90690ec01..2af17b718 100644 --- a/packages/ui/src/stellar/StablecoinControls.svelte +++ b/packages/ui/src/stellar/StablecoinControls.svelte @@ -79,7 +79,7 @@ From 4074aee14874003f280eaf6652a840e2c72b6efe Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:35:28 +0200 Subject: [PATCH 05/25] regenerate tests --- packages/core/stellar/src/fungible.test.ts.md | 632 +- .../core/stellar/src/fungible.test.ts.snap | Bin 1612 -> 1143 bytes packages/core/stellar/src/fungible.ts | 4 +- .../core/stellar/src/non-fungible.test.ts.md | 1066 +- .../stellar/src/non-fungible.test.ts.snap | Bin 2222 -> 1583 bytes .../core/stellar/src/zip-scaffold.test.ts.md | 10456 ---------------- .../stellar/src/zip-scaffold.test.ts.snap | Bin 8516 -> 0 bytes 7 files changed, 343 insertions(+), 11815 deletions(-) delete mode 100644 packages/core/stellar/src/zip-scaffold.test.ts.md delete mode 100644 packages/core/stellar/src/zip-scaffold.test.ts.snap diff --git a/packages/core/stellar/src/fungible.test.ts.md b/packages/core/stellar/src/fungible.test.ts.md index dd2f19fbc..e00edb414 100644 --- a/packages/core/stellar/src/fungible.test.ts.md +++ b/packages/core/stellar/src/fungible.test.ts.md @@ -12,8 +12,9 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ - use stellar_fungible::{self as fungible, FungibleToken};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -21,47 +22,15 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ + type ContractType = Base;␊ ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ }␊ ` @@ -73,8 +42,9 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ - use stellar_fungible::{self as fungible, burnable::FungibleBurnable, FungibleToken};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -82,63 +52,24 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ + type ContractType = Base;␊ ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ }␊ ␊ //␊ // Extensions␊ //␊ ␊ + #[default_impl]␊ #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ + impl FungibleBurnable for MyToken {}␊ ` ## fungible pausable @@ -149,72 +80,38 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, FungibleToken};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + ownable::set_owner(e, &owner);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ + type ContractType = Base;␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ }␊ ␊ #[when_not_paused]␊ fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ }␊ }␊ ␊ @@ -228,22 +125,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## fungible burnable pausable @@ -254,72 +149,38 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, burnable::FungibleBurnable, FungibleToken};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + ownable::set_owner(e, &owner);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ + type ContractType = Base;␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ }␊ ␊ #[when_not_paused]␊ fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ }␊ }␊ ␊ @@ -331,12 +192,12 @@ Generated by [AVA](https://avajs.dev). impl FungibleBurnable for MyToken {␊ #[when_not_paused]␊ fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ + Base::burn(e, &from, amount);␊ }␊ ␊ #[when_not_paused]␊ fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ + Base::burn_from(e, &spender, &from, amount);␊ }␊ }␊ ␊ @@ -350,22 +211,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## fungible preminted @@ -376,8 +235,9 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -385,48 +245,16 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, recipient: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 1000000000000000000000);␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + Base::mint(e, &recipient, 1000000000000000000000);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ + type ContractType = Base;␊ ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ }␊ ` @@ -438,8 +266,9 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ - use stellar_fungible::{self as fungible, FungibleToken};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -447,47 +276,15 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ + type ContractType = Base;␊ ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ }␊ ` @@ -499,72 +296,25 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ ␊ #[contract]␊ pub struct MyToken;␊ ␊ #[contractimpl]␊ impl MyToken {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ + type ContractType = Base;␊ ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ }␊ ` @@ -576,10 +326,10 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ - use stellar_fungible::{self as fungible, FungibleToken};␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -587,49 +337,25 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + ownable::set_owner(e, &owner);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ + type ContractType = Base;␊ ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ + }␊ ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ + //␊ + // Utils␊ + //␊ ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## fungible full @@ -640,75 +366,45 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + ownable::set_owner(e, &owner);␊ + }␊ + ␊ + #[only_owner]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128) {␊ + Base::mint(e, &account, amount);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ + type ContractType = Base;␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ }␊ ␊ #[when_not_paused]␊ fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ }␊ }␊ ␊ @@ -720,22 +416,12 @@ Generated by [AVA](https://avajs.dev). impl FungibleBurnable for MyToken {␊ #[when_not_paused]␊ fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ + Base::burn(e, &from, amount);␊ }␊ ␊ #[when_not_paused]␊ fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - #[when_not_paused]␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ + Base::burn_from(e, &spender, &from, amount);␊ }␊ }␊ ␊ @@ -749,22 +435,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## fungible full - complex name @@ -775,75 +459,45 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct CustomToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum CustomTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl CustomToken {␊ pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "Custom $ Token"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ + Base::set_metadata(e, 18, String::from_str(e, "Custom $ Token"), String::from_str(e, "MTK"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + ownable::set_owner(e, &owner);␊ + }␊ + ␊ + #[only_owner]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128) {␊ + Base::mint(e, &account, amount);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for CustomToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ + type ContractType = Base;␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ }␊ ␊ #[when_not_paused]␊ fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ }␊ }␊ ␊ @@ -855,22 +509,12 @@ Generated by [AVA](https://avajs.dev). impl FungibleBurnable for CustomToken {␊ #[when_not_paused]␊ fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ + Base::burn(e, &from, amount);␊ }␊ ␊ #[when_not_paused]␊ fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for CustomToken {␊ - #[when_not_paused]␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ + Base::burn_from(e, &spender, &from, amount);␊ }␊ }␊ ␊ @@ -884,20 +528,18 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, CustomTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, CustomTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for CustomToken {}␊ ` diff --git a/packages/core/stellar/src/fungible.test.ts.snap b/packages/core/stellar/src/fungible.test.ts.snap index aa6df0d2db338e392151dd16bf6cf942478bbe36..642146586c222cb0b163f0ab8193c6c1f9e9c9ee 100644 GIT binary patch literal 1143 zcmV--1c>`VRzVB!Wi|51n2Q_C*w9ZS=t@)yMESCj1)`$AnQ1 zeN7m{68b`lA?C0zxR%)03clUi+}Yf6);~Pr!c#gJI8LGns7%mP!3jbap9Fye$#FC0{;px}uf@e&5hH|(*x%1r zEFNDGk4~>=aC2~`30G8bWfo?$TjZi{x&i7jPE<40?`+nf{=uDvh596%tHRXIh2@<*-7{JOHRO=cE8WgC!fK(+zB z84YNPrO(XDUl0)UVrWfP+d(s^XIsm2%$5kVdDbMn*q!z$+YWzAI=mczJW3K#u1U}^ zDEXz<*neEyLCM^QAudSb)0h%diSuuRnRp1T0{Tgq94e&)2GjawTM-5zm_0JlO`OVUp#iTL& ziP9pvl*&{@Zl2C$&H&s6OKV;qt(i`_r8c*n+Dzxv(wpm`H`8&o6z3W!PA#k>=RXT% JE+-f;002gqFf#xE literal 1612 zcmV-S2DAA=RzV-H$N90 z^l3?+vLA~G00000000B+TS;%+HWYT3B!HR}=r!o!d0Y%;0C~LR63B3Y)^UNLO&!z; zy11^ukvKMMid0C-O9LNr?Y~I>P0>G4^k4MQdqM5eNMk$RCNXs|G!pq1>gj#oqn=-n zhLn4I^7lWmB8p-*M3Q(gir5YrQcNU6q1@$4zIyHZNTDBZ&wMa*XGS0I-J1F2{eN#Q zEWqZ|M?cJeLp;nRo`39PrpSn3;lPu}&#Z+7SmQy6l$HkvMD4;xh}lm#3^64PHWj86 z3E1R<4-tbk&Xho&lJLvoV0o};&CPBx?n>ovTUI17$Yh`^{XNHd`GxNbETs)zI7kkjLpL{OsH&75_dmlD$vAesAb!&m9qGrot}@rq^5ZDsPe ztuPvbR3h>eJUNO-R0e1Rwk`cPyA3Zb002F2#K3hu&XW0D^socnb++G6$LOQ5EWirs z^>-YJm7D$QVH=hnX6`x8NbtawO6cpIRAr~%cK_Meoqm5MR(5P1w-{dWA4Xi%Tp2M? zTp{YpC=BUQ;l=#tK$eytHhNQ?(N9gtzlqEN1%OG&rlr5`9reAl|0 zW22B~LsyHkp{sZ{Gk?N$^{yUT*id))qO3~Q6w7fcBT*O%zJG@2Q#Hv3O7^iEF-53L zvA?57Mh}*oP0f{!Kf6q9gTOJHee96{QCWEhq6f<-ywJt8dY~{A;EJ&1Jw8ERGOI32 z#+mQz(z1E|JWr%Z@gf@y!$>gH2o`ttDq!*Rop&58vXyY0ENy<7UNJyofW!ca0TKfw z1&~+^3p(MiA1cfw;Y{ialaoi5PMh7r&aTVXe{)%PANDdp{f4lyjlR5#b6%S^6o%nXA ze>N5oFVMC%x8-9&_Hl2GhetN7QO>XpkMJ_^zprNt9EzS! zfr&!Qq99YZt~I1l{hXml?Q%hW!#=FSlHJM-PETp-+51{3jy~`}DlX6t?)3+RNfje@ ze;~2yb>nA~0f3=JS4@lMwra1`_B61ildUo&%8;mYkf`U1&A@%17=JVa?rqNZ5Y;s;jS66#K#Q2PUX7-CQLI`LePyF8*k3^7QoI;GT1 zMu~KoT}`bh=}vs9QifR_X=MO&oHBNmL?5@UM;xn(vavXf*iBuU-1G6p$=T}R{jqJN z3B2|0{R(gW=&s?d=ii7joYrt!Fr3zKTEl4#r=2EFdqEq6?QwdQ`-PLOn4)b&Q&5LwK zfKdaCqOB(*stxV7|5U)@ueY(?)~h;~YJGlWO0(TGQ}%4Gn+e-#@2@ z6`b~a=<=lRp@v>eGrcg~gocdVBr;;U39qx8@J+XO+C98UyFR8F(==mF>C&9)zq>xI zRDduK9uGo_4`ZkpF9<*Wuu>5IX9_}75S9zVwMZ%+Txn5QQu$_uVM)NW6^Dsw0H47- zSR{_GyD1b+p=b(4Qz%|mp*Ws#OtE-b#o}4k+7yfv3&uvEG)3bW^D~9x#Vyao;_-j~ KBYA3+TmS$lp$=RC diff --git a/packages/core/stellar/src/fungible.ts b/packages/core/stellar/src/fungible.ts index 3a549a776..c7c144e0d 100644 --- a/packages/core/stellar/src/fungible.ts +++ b/packages/core/stellar/src/fungible.ts @@ -13,14 +13,14 @@ import { printContract } from './print'; import { toByteArray, toUint } from './utils/convert-strings'; export const defaults: Required = { - name: 'MyFungible', + name: 'MyToken', symbol: 'MTK', burnable: false, pausable: false, upgradeable: false, premint: '0', mintable: false, - access: commonDefaults.access, // TODO: Determine whether Access Control options should be visible in the UI before they are implemented as modules + access: commonDefaults.access, info: commonDefaults.info, } as const; diff --git a/packages/core/stellar/src/non-fungible.test.ts.md b/packages/core/stellar/src/non-fungible.test.ts.md index 00d145d86..56b8c2b30 100644 --- a/packages/core/stellar/src/non-fungible.test.ts.md +++ b/packages/core/stellar/src/non-fungible.test.ts.md @@ -13,6 +13,7 @@ Generated by [AVA](https://avajs.dev). #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{Base, NonFungibleToken};␊ ␊ #[contract]␊ @@ -21,63 +22,18 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Base;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ` @@ -90,6 +46,7 @@ Generated by [AVA](https://avajs.dev). #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{Base, burnable::NonFungibleBurnable, NonFungibleToken};␊ ␊ #[contract]␊ @@ -98,79 +55,27 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Base;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ // Extensions␊ //␊ ␊ + #[default_impl]␊ #[contractimpl]␊ - impl NonFungibleBurnable for MyToken {␊ - fn burn(e: &Env, from: Address, token_id: u32) {␊ - Self::ContractType::burn(e, &from, token_id);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ - Self::ContractType::burn_from(e, &spender, &from, token_id);␊ - }␊ - }␊ + impl NonFungibleBurnable for MyToken {}␊ ` ## non-fungible pausable @@ -181,41 +86,32 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{Base, NonFungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ @@ -226,44 +122,6 @@ Generated by [AVA](https://avajs.dev). fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -276,22 +134,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible burnable pausable @@ -302,41 +158,32 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{Base, burnable::NonFungibleBurnable, NonFungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ @@ -347,44 +194,6 @@ Generated by [AVA](https://avajs.dev). fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -414,22 +223,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible mintable @@ -440,10 +247,11 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ - use stellar_non_fungible::{Base, Base::mint, NonFungibleToken};␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_non_fungible::{Base, NonFungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -451,71 +259,33 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ ␊ - pub fn mint(e: &Env, to: Address, token_id: u32) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ + #[only_owner]␊ + pub fn mint(e: &Env, to: Address, token_id: u32, caller: Address) {␊ Base::mint(e, &to, token_id);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Base;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ + }␊ ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ + //␊ + // Utils␊ + //␊ ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible enumerable @@ -538,63 +308,18 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Enumerable;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -614,13 +339,14 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ Base, consecutive::{NonFungibleConsecutive, Consecutive}, ContractOverrides,␊ NonFungibleToken␊ };␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -628,70 +354,24 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ ␊ - pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ + #[only_owner]␊ + pub fn batch_mint(e: &Env, to: Address, amount: u32, caller: Address) -> u32 {␊ Consecutive::batch_mint(e, &to, amount)␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Consecutive;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -700,6 +380,14 @@ Generated by [AVA](https://avajs.dev). ␊ #[contractimpl]␊ impl NonFungibleConsecutive for MyToken {}␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible consecutive burnable @@ -710,13 +398,14 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ Base, burnable::NonFungibleBurnable, consecutive::{NonFungibleConsecutive, Consecutive},␊ ContractOverrides, NonFungibleToken␊ };␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -724,70 +413,24 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ ␊ - pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ + #[only_owner]␊ + pub fn batch_mint(e: &Env, to: Address, amount: u32, caller: Address) -> u32 {␊ Consecutive::batch_mint(e, &to, amount)␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Consecutive;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -795,18 +438,19 @@ Generated by [AVA](https://avajs.dev). //␊ ␊ #[contractimpl]␊ - impl NonFungibleBurnable for MyToken {␊ - fn burn(e: &Env, from: Address, token_id: u32) {␊ - Self::ContractType::burn(e, &from, token_id);␊ - }␊ + impl NonFungibleConsecutive for MyToken {}␊ ␊ - fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ - Self::ContractType::burn_from(e, &spender, &from, token_id);␊ - }␊ - }␊ + #[default_impl]␊ + #[contractimpl]␊ + impl NonFungibleBurnable for MyToken {}␊ ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ #[contractimpl]␊ - impl NonFungibleConsecutive for MyToken {}␊ + impl Ownable for MyToken {}␊ ` ## non-fungible consecutive pausable @@ -817,51 +461,41 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ Base, consecutive::{NonFungibleConsecutive, Consecutive}, ContractOverrides,␊ NonFungibleToken␊ };␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ ␊ #[when_not_paused]␊ - pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ + #[only_owner]␊ + pub fn batch_mint(e: &Env, to: Address, amount: u32, caller: Address) -> u32 {␊ Consecutive::batch_mint(e, &to, amount)␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Consecutive;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ @@ -872,44 +506,6 @@ Generated by [AVA](https://avajs.dev). fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -929,22 +525,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible consecutive burnable pausable @@ -955,51 +549,41 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ Base, burnable::NonFungibleBurnable, consecutive::{NonFungibleConsecutive, Consecutive},␊ ContractOverrides, NonFungibleToken␊ };␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ ␊ #[when_not_paused]␊ - pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ + #[only_owner]␊ + pub fn batch_mint(e: &Env, to: Address, amount: u32, caller: Address) -> u32 {␊ Consecutive::batch_mint(e, &to, amount)␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Consecutive;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ @@ -1010,44 +594,6 @@ Generated by [AVA](https://avajs.dev). fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -1080,22 +626,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible sequential @@ -1107,6 +651,7 @@ Generated by [AVA](https://avajs.dev). #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{Base, NonFungibleToken};␊ ␊ #[contract]␊ @@ -1115,63 +660,18 @@ Generated by [AVA](https://avajs.dev). #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Base;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ` @@ -1183,88 +683,34 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{Base, NonFungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_upgradeable::UpgradeableInternal;␊ use stellar_upgradeable_macros::Upgradeable;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[derive(Upgradeable)]␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Base;␊ ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -1273,13 +719,13 @@ Generated by [AVA](https://avajs.dev). ␊ impl UpgradeableInternal for MyToken {␊ fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ + ownable::enforce_owner_auth(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible with compatible options @@ -1290,55 +736,44 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ Base, burnable::NonFungibleBurnable, ContractOverrides,␊ enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ };␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ use stellar_upgradeable::UpgradeableInternal;␊ use stellar_upgradeable_macros::Upgradeable;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[derive(Upgradeable)]␊ #[contract]␊ pub struct MyToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl MyToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "MyToken");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ ␊ + #[only_owner]␊ #[when_not_paused]␊ - pub fn non_sequential_mint(e: &Env, to: Address, token_id: u32) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ + pub fn mint(e: &Env, to: Address, token_id: u32) {␊ Enumerable::non_sequential_mint(e, &to, token_id);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for MyToken {␊ type ContractType = Enumerable;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ @@ -1349,44 +784,6 @@ Generated by [AVA](https://avajs.dev). fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -1416,11 +813,7 @@ Generated by [AVA](https://avajs.dev). ␊ impl UpgradeableInternal for MyToken {␊ fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ + ownable::enforce_owner_auth(e);␊ }␊ }␊ ␊ @@ -1430,22 +823,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible - complex name @@ -1456,41 +847,32 @@ Generated by [AVA](https://avajs.dev). // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ #![no_std]␊ ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{Base, burnable::NonFungibleBurnable, NonFungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ use stellar_pausable_macros::when_not_paused;␊ ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ #[contract]␊ pub struct CustomToken;␊ ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum CustomTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ #[contractimpl]␊ impl CustomToken {␊ pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "Custom $ Token"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ + let uri = String::from_str(e, "www.mytoken.com");␊ + let name = String::from_str(e, "Custom $ Token");␊ + let symbol = String::from_str(e, "MTK");␊ + Base::set_metadata(e, uri, name, symbol);␊ + ownable::set_owner(e, &owner);␊ }␊ }␊ ␊ + #[default_impl]␊ #[contractimpl]␊ impl NonFungibleToken for CustomToken {␊ type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ @@ -1501,44 +883,6 @@ Generated by [AVA](https://avajs.dev). fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ }␊ ␊ //␊ @@ -1568,20 +912,18 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_owner]␊ fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, CustomTokenError::Unauthorized);␊ - }␊ pausable::pause(e, &caller);␊ }␊ ␊ + #[only_owner]␊ fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, CustomTokenError::Unauthorized);␊ - }␊ pausable::unpause(e, &caller);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for CustomToken {}␊ ` diff --git a/packages/core/stellar/src/non-fungible.test.ts.snap b/packages/core/stellar/src/non-fungible.test.ts.snap index 8f39a6af5e9dca400dd73681a078e0352aa3d943..ca49c83e13603377ee0fe4cf2a7fc9dc2fe94af9 100644 GIT binary patch literal 1583 zcmV+~2GIFIRzVf7`JI$iA z;vNlR@gIu_00000000B+Tu*Q0G!!rGcK?JE7led_yxXN}QTj*A&1Dy{rAXDXbftDB zpc*1i;uY%}>Zt&l~+L&hc?(Dr#9FZEFedntxQ4IqT^v7C82O+JtsmgqY$cPdTm09 zQb?p!4NF(kki$?j{JpKfP@9$Vg7~?`iw>n7iqPVZ^osDas7b?Gp@S!?fV2^W91ADH0?FYcYb7kO)28iQTQ1SDI?^_( z2O8D3>a{xXDeEzyIE!(2C>_X~hiP}VEURPX<2_MFI%p=0pvob?@ToTBHy37l$QO2k zP;tZKywF6%^^6E>V?&PGowmS)2b6GGFm%(!D9~H4iMJRfygJ@){;9>=?>A>U-YNo0 zm7%RX8qYZneSkQ~OKh;0T~rGcy`dyLlVzCc)e840ii;u}Jj<^-)iRlG4q@vU6NgZd zKEd7?puh&{yFidTZCas0{<^N0Lg{NuiRJWa%m^YMFCF%{yO9?A1PF!*_p#z6X^5_m zY#^w1bApKzcn~BXKT!R*k3%0qwF3~As%@|=wc21=&{9d)4wvkrBXyMhyX{hwM1c0LMs&2{>IU1VQ z(t0im!VtoS6QW8U>1o@1H3lG>t>p|?>4M|r1v^VXz|1wyte%R@ zuZ$`!4OK8nBFt*i=1jvCcUIHpRxvE>Zjbj)F=K&;xvf|b!gIypJKb{GvEsVZ4^WoL zuupJ?y>#6O^X#S4x=h4-T9itMVmA@NY1Q=quNJ2N%C_6qoEJ_-XL-O87ad(;H(3 z;sLv5Z)-sO-z@{g*OaN6{k5sZ^2&V+1My~O@EYt44!0UHn_LsH$<_Yo!WG>BMsep? zjZyq~$1sZNT-q7JVF-sI9ENaA7~wESkw$zJ$uPXBV|e<31CC!Ll9Hj&e41)LO`V!! z0==K6_H5uM@exBFt~tN=n>Or!n$->bH~P7p;vop1mp8jaU`9|_Tf4iWP=vO!!N!79 zyrLAZHq0VpI=>pIU>~X0ih)#3JRF8!Bmu(?O2o0^%osByD1%_R!l7LQVB%Be;>-xS zic#lj?rVUSf{t5)5RHW-&HdnJ{i1=}57vx=+vPJfHQPHQIcIo%(=!IEryr|pmNqpD z*dY&ntJd97VoAnnxhQeo=)rI8u<78nyGj3;^v^V=e@1nXdcygb$xKZ^W~%+Z#%b45 zLL1_CI*C;rUlJOX#pYxGS^wC7JK=&x1L>erWnw8|)<*#DgNdbvuSC51ePI3pz|_#L he&-tMHJ*P0&~JIeQ%1m3G_!WB{{bj41C8ZX003t81_1y7 literal 2222 zcmV;f2vPSzRzV}#n-A?6g%O> zWpup~3m=OJ00000000B+oy%|AHXO&Z4Z~KoXi@AkV8FmQMPL_-)5lF&3bbhkbX%LE zNwG(f3A9BgLS<4VsXP|=u;X^vc>}gXcisM~op$YM*MWLcwj?{L6Fc!Qht}4E{P8D| z{Ct0W_;sh{vGz;x%b!HL(j&Zuh1*7y(WOp6cifgoTtTrfc9|4EzIGfe@wXq=-kbYi zPW`>}{@nF5D@qOaC6m3f4d6=V3##swfw8p69xGe?x{#Nr&^OmtN|AI1GNN3wH0zr_7F0_U7 z(z1^3I}Rs8n5Z4!mwj@(zGtF!x@V$I$z8hBTZs~gaTuSzh2-j3ZGV5i**%mhlV+QBYmJiYVnfQ$ zZ+%&7G*&_rdPc9LIQ@=69mf00C=Y#-KEEyXWi>3%iV>dfP{j5r;Wq0eB{xxMzU?{| z3hv%%prv~#exjOBHi_4g=Zq!cnlW}0;~Y)27-u%3#Vzb%+9sK;q03Wi!e)9T0c2uhA z`C%SSOjTt`jLJSbnJ>-3YzEvL-(`0gx3TAyx!FX_Cmc(rA1W@yh~{TGCXYn@A7?U( z$NtvrE@`f*6Mn8+KzBk0We5&P@+7;<#C-7ZpSc@@lh&6?|6!@#(I*Pj_Yu17W0muT5{h&a~OIw;BM7J8z~1ZrNIZ)Gh&_ybdo@=fKyzE6qkO> zA;r&^E*MfICATcC4}B0H1mXbV0OA1R0OA1R0OB|)aTv?XiVD^bB%#7(RHy?(dzGXQ zF6dZPYZP!=(T*RZU5rvn8^QASFW~g}t@SYtDQ}zy>?$ES9|xkCb7lVkR4wXy{0DG4Bz#r`HtRuTkJ9}jT1(f^m`J|TGo*uUI$^3 zI@?4~<4+~`*j?P_Ojy?bE}=GMGCV_^6~hPt5N&+@Wc`^H+mBXr#?RMl;ptlAc<>qe zX4_bJ;Slcbk@_0*4^6b@F-lDIkhFpw6Rp2ewsYxXPy4vR9TRPEr=jlV#OHPO{ze%R z8g!HF)>RJiYyF(!K<+Z`{y-eGif)^w=r}qF4?@P&ka376O(7Y_JEY!dx>U%JY#L2L zWPLGwVkzWYG@4#`(f1;6PaR|rHxbwy*c;dz*n0-p`?++z3hYkkrI@v&<}+9LzQ>lK zQF3j6fy3ijVcXdAO8Rw=$i8<6t(Nt)J84hbXp#e;v}L^(-iyR83p@w42oglrv_9MI zM5&|s)z}Ix@2D(!xSUsx)p^IVbkxz3?v?o?d|ji=25N9XXVo=n$p@X2YMX;k3&YeE zD(C9coXWX>6;#gK2E0K8fd~Q-1R@ATQ1k~P2t*Kwpz|k!#y0>_-mISSxJ0!f(=}WH zQWT`$+5yOqlvLCqREXPZOq6X_=E=qo~SAh$ycV&zU+sfd`r@t;2DN z)l<;B)KOaaQ?Ae9=_V(y0+$n8)yukeHhX!(8d#_TE{bTJCUpoh5M&_8K#+lDxm_?B z7=zq?4&+oMm+%slo6Dixn{&LJ&LJHfc+ytaMR9n`(vMXlo{@CYW@_=q9^u?|h%hw| zTGo-)9j~Xym0`)}-XI?uFsc6SU?%e}APpc5APpc5AkAzbO<81H@7lMA_BC1mk?(M_ zX&4{pm`Qc7q#a0iuK=2iFLKbdVw@vrI!XLXgCor^c@$d9PJ{@ReCZVdRhVEQBS8@D z?1E@5EZe(wMJNq-SwLl0o%0>^MB`~|`g^DsBnFYlbgdN`CyZ*D1C0WW0*wNV0*%f# zjaGLqT8B}FpYvt-S+Q}&<-cMvN8xu|L36z*N9gz*N9gz*N9g zz|;$jsjwf(&6EJ=X`4)SU3qv36v5j zB~VHiQYlqnKCA(RHGost3yX#wNCBS|M?~%1 zux|Ej(`UIj+34NP#FA48;3Y{H4sGj{eIrNNAqlrLGG<}b6NAKk{l6S>|9gFgiF;g| zPY#(HRI*xSEc;WQ{=6WN8z46(JQGRPylilyYD!WmA5U~P(z;gD3(Imwo6AplKUSER z1o92!8^|}1Z|6h4!MvnN&r2HDcDbO(Pfw4-lrNa_H6^YYX{p#Jxwz3%7&P+8LD=&Z z>|t_{3m3Vthso^xZ24LsB15MQ!?x4@6Y8)5B1`sxY wumOY(AZ&2)!v=$05rhuT<|oYuJr6onxx1f8L933>J(YU(e|(Dd&}pgw02}@>&j0`b diff --git a/packages/core/stellar/src/zip-scaffold.test.ts.md b/packages/core/stellar/src/zip-scaffold.test.ts.md deleted file mode 100644 index 531159a4c..000000000 --- a/packages/core/stellar/src/zip-scaffold.test.ts.md +++ /dev/null @@ -1,10456 +0,0 @@ -# Snapshot report for `src/zip-scaffold.test.ts` - -The actual snapshot is saved in `zip-scaffold.test.ts.snap`. - -Generated by [AVA](https://avajs.dev). - -## fungible simple - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ - use stellar_fungible::{self as fungible, FungibleToken};␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, ());␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible full - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - #[when_not_paused]␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - #[when_not_paused]␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible burnable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible mintable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible burnable mintable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible burnable pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - #[when_not_paused]␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible mintable pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - #[when_not_paused]␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable simple - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, FungibleToken};␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable full - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - #[when_not_paused]␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - #[when_not_paused]␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable burnable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable mintable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable burnable mintable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable burnable pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{␊ - self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ - };␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleBurnable for MyToken {␊ - #[when_not_paused]␊ - fn burn(e: &Env, from: Address, amount: i128) {␊ - fungible::burnable::burn(e, &from, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ - fungible::burnable::burn_from(e, &spender, &from, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## fungible upgradable mintable pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyToken;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyTokenError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyToken {␊ - pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ - fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ - fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl FungibleToken for MyToken {␊ - fn total_supply(e: &Env) -> i128 {␊ - fungible::total_supply(e)␊ - }␊ - ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ - }␊ - ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ - ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ - ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl FungibleMintable for MyToken {␊ - #[when_not_paused]␊ - fn mint(e: &Env, account: Address, amount: i128) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - fungible::mintable::mint(e, &account, amount);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyToken {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyToken {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyTokenError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyToken, MyTokenClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ - let client = MyTokenClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible simple - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ - use stellar_non_fungible::{Base, NonFungibleToken};␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, ());␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible full except sequential mintable enumerable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_non_fungible::{␊ - Base, burnable::NonFungibleBurnable, consecutive::{NonFungibleConsecutive, Consecutive},␊ - ContractOverrides, NonFungibleToken␊ - };␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyNFTError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - ␊ - #[when_not_paused]␊ - pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - Consecutive::batch_mint(e, &to, amount)␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Consecutive;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl NonFungibleBurnable for MyNFT {␊ - #[when_not_paused]␊ - fn burn(e: &Env, from: Address, token_id: u32) {␊ - Self::ContractType::burn(e, &from, token_id);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ - Self::ContractType::burn_from(e, &spender, &from, token_id);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleConsecutive for MyNFT {}␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyNFT {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyNFT {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible burnable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ - use stellar_non_fungible::{Base, burnable::NonFungibleBurnable, NonFungibleToken};␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl NonFungibleBurnable for MyNFT {␊ - fn burn(e: &Env, from: Address, token_id: u32) {␊ - Self::ContractType::burn(e, &from, token_id);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ - Self::ContractType::burn_from(e, &spender, &from, token_id);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, ());␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible consecutive - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ - use stellar_non_fungible::{␊ - Base, consecutive::{NonFungibleConsecutive, Consecutive}, ContractOverrides,␊ - NonFungibleToken␊ - };␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - ␊ - pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - Consecutive::batch_mint(e, &to, amount)␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Consecutive;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl NonFungibleConsecutive for MyNFT {}␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_non_fungible::{Base, NonFungibleToken};␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyNFTError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyNFT {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible upgradeable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_non_fungible::{Base, NonFungibleToken};␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyNFTError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyNFT {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible sequential - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ - use stellar_non_fungible::{Base, NonFungibleToken};␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, ());␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible burnable pausable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_non_fungible::{Base, burnable::NonFungibleBurnable, NonFungibleToken};␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyNFTError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Base;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl NonFungibleBurnable for MyNFT {␊ - #[when_not_paused]␊ - fn burn(e: &Env, from: Address, token_id: u32) {␊ - Self::ContractType::burn(e, &from, token_id);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ - Self::ContractType::burn_from(e, &spender, &from, token_id);␊ - }␊ - }␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyNFT {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible enumerable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ - use stellar_default_impl_macro::default_impl;␊ - use stellar_non_fungible::{␊ - Base, ContractOverrides, enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ - };␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Enumerable;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[default_impl]␊ - #[contractimpl]␊ - impl NonFungibleEnumerable for MyNFT {}␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, ());␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible burnable enumerable - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ - use stellar_default_impl_macro::default_impl;␊ - use stellar_non_fungible::{␊ - Base, burnable::NonFungibleBurnable, ContractOverrides,␊ - enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ - };␊ - ␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Enumerable;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl NonFungibleBurnable for MyNFT {␊ - fn burn(e: &Env, from: Address, token_id: u32) {␊ - Self::ContractType::burn(e, &from, token_id);␊ - }␊ - ␊ - fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ - Self::ContractType::burn_from(e, &spender, &from, token_id);␊ - }␊ - }␊ - ␊ - #[default_impl]␊ - #[contractimpl]␊ - impl NonFungibleEnumerable for MyNFT {}␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, ());␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] - -## nonfungible full except consecutive - -> Snapshot 1 - - [ - `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ - ␊ - ␊ - use soroban_sdk::{␊ - Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ - symbol_short␊ - };␊ - use stellar_default_impl_macro::default_impl;␊ - use stellar_non_fungible::{␊ - Base, burnable::NonFungibleBurnable, ContractOverrides,␊ - enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ - };␊ - use stellar_pausable::{self as pausable, Pausable};␊ - use stellar_pausable_macros::when_not_paused;␊ - use stellar_upgradeable::UpgradeableInternal;␊ - use stellar_upgradeable_macros::Upgradeable;␊ - ␊ - const OWNER: Symbol = symbol_short!("OWNER");␊ - ␊ - #[derive(Upgradeable)]␊ - #[contract]␊ - pub struct MyNFT;␊ - ␊ - #[contracterror]␊ - #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ - #[repr(u32)]␊ - pub enum MyNFTError {␊ - Unauthorized = 1,␊ - }␊ - ␊ - #[contractimpl]␊ - impl MyNFT {␊ - pub fn __constructor(e: &Env, owner: Address) {␊ - Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ - e.storage().instance().set(&OWNER, &owner);␊ - }␊ - ␊ - #[when_not_paused]␊ - pub fn sequential_mint(e: &Env, to: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - owner.require_auth();␊ - Enumerable::sequential_mint(e, &to);␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl NonFungibleToken for MyNFT {␊ - type ContractType = Enumerable;␊ - ␊ - fn owner_of(e: &Env, token_id: u32) -> Address {␊ - Self::ContractType::owner_of(e, token_id)␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer(e, &from, &to, token_id);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ - Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ - }␊ - ␊ - fn balance(e: &Env, owner: Address) -> u32 {␊ - Self::ContractType::balance(e, &owner)␊ - }␊ - ␊ - fn approve(␊ - e: &Env,␊ - approver: Address,␊ - approved: Address,␊ - token_id: u32,␊ - live_until_ledger: u32,␊ - ) {␊ - Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ - }␊ - ␊ - fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ - Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ - }␊ - ␊ - fn get_approved(e: &Env, token_id: u32) -> Option
{␊ - Self::ContractType::get_approved(e, token_id)␊ - }␊ - ␊ - fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ - Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ - }␊ - ␊ - fn name(e: &Env) -> String {␊ - Self::ContractType::name(e)␊ - }␊ - ␊ - fn symbol(e: &Env) -> String {␊ - Self::ContractType::symbol(e)␊ - }␊ - ␊ - fn token_uri(e: &Env, token_id: u32) -> String {␊ - Self::ContractType::token_uri(e, token_id)␊ - }␊ - }␊ - ␊ - //␊ - // Extensions␊ - //␊ - ␊ - #[contractimpl]␊ - impl NonFungibleBurnable for MyNFT {␊ - #[when_not_paused]␊ - fn burn(e: &Env, from: Address, token_id: u32) {␊ - Self::ContractType::burn(e, &from, token_id);␊ - }␊ - ␊ - #[when_not_paused]␊ - fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ - Self::ContractType::burn_from(e, &spender, &from, token_id);␊ - }␊ - }␊ - ␊ - #[default_impl]␊ - #[contractimpl]␊ - impl NonFungibleEnumerable for MyNFT {}␊ - ␊ - //␊ - // Utils␊ - //␊ - ␊ - impl UpgradeableInternal for MyNFT {␊ - fn _require_auth(e: &Env, operator: &Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != *operator {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - operator.require_auth();␊ - }␊ - }␊ - ␊ - #[contractimpl]␊ - impl Pausable for MyNFT {␊ - fn paused(e: &Env) -> bool {␊ - pausable::paused(e)␊ - }␊ - ␊ - fn pause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::pause(e, &caller);␊ - }␊ - ␊ - fn unpause(e: &Env, caller: Address) {␊ - let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ - if owner != caller {␊ - panic_with_error!(e, MyNFTError::Unauthorized);␊ - }␊ - pausable::unpause(e, &caller);␊ - }␊ - }␊ - `, - `#![cfg(test)]␊ - ␊ - extern crate std;␊ - ␊ - use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ - ␊ - use crate::contract::{ MyNFT, MyNFTClient };␊ - ␊ - #[test]␊ - fn initial_state() {␊ - let env = Env::default();␊ - ␊ - let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ - let client = MyNFTClient::new(&env, &contract_addr);␊ - ␊ - assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ - }␊ - ␊ - // Add more tests bellow␊ - `, - `#![no_std]␊ - #![allow(dead_code)]␊ - ␊ - mod contract;␊ - mod test;␊ - `, - `[package]␊ - name = "non-fungible-contract"␊ - edition.workspace = true␊ - license.workspace = true␊ - publish = false␊ - version.workspace = true␊ - ␊ - [lib]␊ - crate-type = ["cdylib"]␊ - doctest = false␊ - ␊ - [dependencies]␊ - stellar-default-impl-macro = { workspace = true }␊ - stellar-non-fungible = { workspace = true }␊ - stellar-pausable = { workspace = true }␊ - stellar-pausable-macros = { workspace = true }␊ - stellar-upgradeable = { workspace = true }␊ - stellar-upgradeable-macros = { workspace = true }␊ - soroban-sdk = { workspace = true }␊ - ␊ - [dev-dependencies]␊ - soroban-sdk = { workspace = true, features = ["testutils"] }␊ - ␊ - `, - `#!/usr/bin/env bash␊ - #␊ - # setup.sh␊ - # ␊ - # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ - ␊ - check_is_installed() {␊ - if ! which "$1" &> /dev/null; then␊ - echo "❌ $1 command not found."␊ - echo "Install $2 and try again, you can find installation guides in the README."␊ - exit 1␊ - fi␊ - }␊ - ␊ - scaffold() {␊ - tmp_folder="tmp"␊ - stellar scaffold init "$tmp_folder"␊ - ␊ - rm -rf "$tmp_folder/contracts"␊ - ␊ - local current_directory␊ - current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ - ␊ - shopt -s dotglob␊ - ␊ - cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ - rm -rf "$current_directory/$tmp_folder"␊ - }␊ - ␊ - init_git(){␊ - git init␊ - git add .␊ - git commit -m "openzeppelin: add wizard output" --quiet␊ - }␊ - ␊ - ␊ - # Update environments.toml: remove original contracts and insert wizard's contract␊ - setup_environment() {␊ - local file="environments.toml"␊ - local temp␊ - temp="$(mktemp)"␊ - ␊ - local in_dev_contracts=0␊ - local skip_entry=0␊ - local contract_entry_inserted=0␊ - insert_contract_entry() {␊ - {␊ - printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ - "client = true" "" \\␊ - "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ - "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ - "# \\\`stellar contract deploy\\\`" \\␊ - "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ - "# TODO add appropriate values for for the constructors arguments" \\␊ - "constructor_args = \\"\\"\\"" \\␊ - "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ - "\\"\\"\\"" \\␊ - ""␊ - } >> "$temp"␊ - }␊ - ␊ - while IFS= read -r line; do␊ - if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ - insert_contract_entry␊ - contract_entry_inserted=1␊ - fi␊ - ␊ - if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - in_dev_contracts=1␊ - skip_entry=0␊ - continue␊ - fi␊ - ␊ - if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ - if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ - skip_entry=1␊ - in_dev_contracts=0␊ - continue␊ - fi␊ - in_dev_contracts=0␊ - skip_entry=0␊ - printf '%s\\n' "$line" >> "$temp"␊ - continue␊ - fi␊ - ␊ - if (( skip_entry )); then␊ - continue␊ - fi␊ - ␊ - if (( in_dev_contracts )); then␊ - if [[ $line =~ ^[[:space:]]*# ]]; then␊ - printf '%s\\n' "$line" >> "$temp"␊ - fi␊ - continue␊ - fi␊ - ␊ - printf '%s\\n' "$line" >> "$temp"␊ - done < "$file"␊ - ␊ - mv "$temp" "$file"␊ - }␊ - ␊ - ␊ - update_cargo() {␊ - cp Cargo.toml Cargo.toml.bak␊ - ␊ - cat < deps.tmp␊ - stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ - soroban-sdk = { version = "22.0.8" }␊ - ␊ - EOF␊ - ␊ - awk '␊ - BEGIN {␊ - inserted = 0␊ - deps = ""␊ - while ((getline line < "deps.tmp") > 0) {␊ - deps = deps line "\\n"␊ - }␊ - close("deps.tmp")␊ - }␊ - /^\\[workspace.dependencies\\]/ {␊ - in_deps = 1␊ - print␊ - if (!inserted) {␊ - printf "%s", deps␊ - inserted = 1␊ - }␊ - next␊ - }␊ - /^\\[/ { in_deps = 0 }␊ - in_deps { next }␊ - { print }␊ - ' Cargo.toml.bak > Cargo.toml␊ - ␊ - rm deps.tmp␊ - rm Cargo.toml.bak␊ - }␊ - ␊ - build_contracts() {␊ - cargo build␊ - }␊ - ␊ - install_npm_dependencies() {␊ - if ! npm install --silent; then␊ - echo "❌ Failed to set up the project."␊ - exit 1␊ - fi␊ - }␊ - ␊ - ␊ - ################␊ - ##### Start ####␊ - ################␊ - ␊ - echo "⚙️ Checking dependencies requirement"␊ - check_is_installed git "Git"␊ - check_is_installed cargo "Rust"␊ - check_is_installed stellar "Scaffold"␊ - check_is_installed docker "Docker"␊ - check_is_installed node "Node"␊ - ␊ - ␊ - if ! [ -f "environments.toml" ]␊ - then␊ - echo "🏗️ Building Scaffold project"␊ - ␊ - scaffold␊ - ␊ - setup_environment␊ - ␊ - update_cargo␊ - ␊ - build_contracts␊ - ␊ - install_npm_dependencies␊ - ␊ - init_git␊ - ␊ - echo "✅ Installation complete" ␊ - else␊ - echo "✅ Scaffold project already initialized."␊ - fi␊ - `, - `# Sample Scaffold Project␊ - ␊ - This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ - ␊ - ## Installing dependencies␊ - ␊ - - See [Git installation guide](https://github.com/git-guides/install-git).␊ - - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ - - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ - - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ - - See [Node installation guide](https://nodejs.org/en/download).␊ - ␊ - ## Initializing the project␊ - ␊ - \`\`\`␊ - bash setup.sh␊ - \`\`\`␊ - ␊ - ## Resolve any TODOs ␊ - ␊ - Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ - ␊ - ␊ - ## Testing the contract␊ - ␊ - \`\`\`␊ - cargo test␊ - \`\`\`␊ - ␊ - ## Deploying the contract␊ - ␊ - \`\`\`␊ - stellar scaffold watch --build-clients␊ - \`\`\`␊ - ␊ - ## Deploying the contract and run the Scaffold UI app␊ - ␊ - \`\`\`␊ - npm run dev␊ - \`\`\`␊ - `, - ] diff --git a/packages/core/stellar/src/zip-scaffold.test.ts.snap b/packages/core/stellar/src/zip-scaffold.test.ts.snap deleted file mode 100644 index ae4e0b0214b68ed0d101a842af3ed1444f97c7d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8516 zcmb8zbyO7p+5m7BkS^(7kdQ8slI~QxJ9la67Fdw(kQP=zx>;&LN;;MlDUn4Qq#It; z-@W&oSMPbxJD)S>ndi(uGvAqKzVm$~v?TxeI|5p}+uksNiZTim=v)&u14bB>UGJK_iqccsWs8J_zt3Ut{pEwzN7RPv9 zbW7-_B8~Cg+T9MTq~cmdkKSnhQOb(Ljj_&58iT#yNjN zZ}^$b_St8PJqGYR0DLtB0s9?tOXdiv_T}aR5dBd6SJvC`35z(xdIxt`d$uz>5GXR5 z*>YQr=b6MvG}U99-bLw8)PnxoG}O$5>*Go@Vp+ZtoV*ieO)@d*hL!6>9OEP;drv|i zWJ5QxQwA>_)>~zr_}-rL*gbulm+2P4+x60+*>ZNe88q%ah-lFNj^n>7d82+4od2Yv z>%&Sphu{~O`Tnw&N^SazC2;n|!iCaP@l=kt!)!+18XUE2-GH@ij${UDu;|tUMTsP$ zC|oLQ%@3PnrfR8+KW>{X7A(F`TgjyB;T)J~k?#t*H}0eg`nzZp+A0B9s!GBGQA;02_?9d|ObEcK;xOgLGUQ0a5v%mx-ccstgS*p`%xwRfENeSk zHj$py1{*Y(z3VY0XB&ND!zSL+GOQ%UM7dPZLsOw1UR7>Q(^~{n)B~-nXundapkN3A zY9);O_ZA>Z!b07tTL5!lEaAKbDm?4liV=Oq_lCT4TJ-jy`>l#t!vP)jVde{_x| zC+)Fs+l?z`!yxz?ytE$|gCcU<+iP8zHkbm+;C*AGO%y8fb?6yzR=a84s(w&`%CpX* z&>-MZlR`D=krZ9}(t>4(*mvyg^ei8v>L)H*#cwtF-7BI*Lrb#?Lb~j^RMThQyXJGj z$BCx+yo6l!&~;E{QZ!X+H?rh&pn6)K0<{xnvm5Uq$(de!Z|y48s%>jxj@Sr%`Q}pX zUS@3y41H(t2o&0}N=SRdQXF_v19V?bAIuNoAncx1-AWz)@Y(fE1uuLb{08whQ2x^* zFE7kqpW%&N>A=uY&34W>xkkPE?D$&&S$8GO)9acmhF7hW*2jfk*8@QIO?jz%#aay| z;TPkA85pX`hVfF0W*H;ygF{ZbCn808sWL*2Ey~=jaPhUsuolO4)tC`@BuGFR$TO!f zB%$~0=vYf8heEJz7LPSm!^&Y|Y(}Fr(mF+Es>?6gDyBbbhHYy|PwQ&VZ$#`u(8XYI zYO2NoE_f0DDGFk+OuVdHVj#2!9=R(HM+IrCj3(XChFLZxR^`=-Vag_m`I=z`g(^tP zl9citKPu{ahz9Qj%1kxpR}OzW9M3Ykw)q3|U0oKJ|xLY{kJ zM%F8(_xX+?C2qxK+AOJg@GuAe*3|Tf@Ur*H%Y|gSD{R67 zLyJ{kh6U!0!Y-{Y(~Lm&O(G!`evnU-ti~Eg;rcd_{+Mp9X>*v19>Q!u37ld_tm1#l zFQzbR^xmq}m_&<zi%tf9Y}r-;0ZNL(F#d5nzLr)1U3(MJ3DLSWe-~e`p25{3* z?tZB4G3NPu`eI49N}n}=zWP(8;*vu**{7CTYc+XSVp=K??^`FAMt410C!2g)qk5r$hFe4OY|X{n^ig{1A znfWPQy!@{mow`TuWQ+w0j#FVj;bNS+Z{iOe()oFc2FC*ok=y86^PD1P zI4gG-DE!M>7YGr8!#U)@C!^E3EWy&cA7RWQqv616vu-Y9=;NVlpsztyGM-Z3u$Gm= zC+Nkxm7>Dg1ky2@jm+mA7cA$_T(!_!D&~HprZs;GiWd4dh;B&VJy37%?dixbTN%J zjH4e?_}~VWW%b!n4uamWVPe=TnIrU}EhPk(1Ei25nqyHn1M*CRFQc|NFLRq`imfTe zIB#(9$S}<{==*1X9|^2x=)}~Nywlt&Vt>Y6c!R@7 zO3^4{dVuxN;wU9$y|{i{2&%0;+JhOFox~HT>D%Sy9&N-%a!pjN#PFfkvDrS8g`>Tu z`NB{#<_JTjNEqXNdbI6K_Kvk^ZnBGk6=xVG!DSFwB;m4Vm=V3ay6fTYwC#o)y+%fV z-HuvZ%%d-yivtc0O|6E>UAf;jH*0yb*`Zx9G=nGA*1~<#waT)&Ma8c9foa+9FtRe7 zv`C6rRww>~8jucqJA|Eoz4eUQ2{}RRaBEYc@fPb9`-XY*^>}1c+MReLN zm{H=g=o}d;skO%EDPq#F8Mv}1!i~*@VcW_-fctu zM)GEXE}_PGE6f(#eT7B+v>{m-p+o2TM9}O9 zRET@VpkQ-{a?Y8pv(yxJp$xSVT^yhv;%&uv5(jabf|0u^FL6qu~o6|aOmp|z+qhL%Et*qYRs%OqJ%(7(hOvNbe zRz=xdpBz%0nab$HC)04uGpuFXiI-Zz5)k6+dmA$9f1%T}YeImkz8!Ey2KVj$LBf(8 zX|mr@EB|PRw`cFO9>fc2w%fJaU`<5&iP>=5B}MMLD7Z~a4MUr!b80>YLFN5?bm3fk zZ0_zaSjH3^l|1{-dho8IExF*Lt=hPuF$OKmg@M(AuP!>G>ZsnJG7h;W78~==EfqpO zP4zK;Ed@esFGyA)IIgXn0FWI*&x(=2z|PG-i393SzE2I)nUFBq5vf5yMipvSMl6e2 zOb~ti$E}42eV+K-;#wj6_g-Z~FS8=v+8Z~6=e6g@Y>(Kyen2N5?#8I~7w ze%Ml_6%cuKLw3z;Xp~E^V`3v%noht!o$i$p?IlAySI8_%r)NC^;_=2US=X95!4ZVA zt>oGPzB3LS@u0X@v5~OfF3nqUsSh^rJT;PqvQ@C>=$MxIUsEmI{G6>D9A+byaw$rR)0Fbb8N}2sNjA!czFbbLzXGKWr|i-c!|JF^ zN@k4aG_4}PX72C>Dr~cQq)xjhL7DvFCXYB9@|t76+&DbfO)lU(4|Ja`q|dd=GIxP| z8cGDZG=)WsN*ws(!H)AXF`-cb1Y=DN*SV@ih)vDHcBkDMZ=IBMtC(4zq=L2bPu8g; z6si3+M?wakrFzZICX)-qX%2`{gND=ss6G4Rh6cSZ)hOIBj%cuTep3CdJmNs&7O;};`;qyCzf1(9ojkcYTL(%3c0b$zx*rNS2B+kvSdjK zED~aBXX{8$?O5HhmQr-bGIW^A>7nL)Zi}(tTy9|bTuYjo*Oz0@`tX=bnEFMz;i(e6 z_RLQ6k*Vgmj<7Ej;wa*J3d661T$d11(w{%m!gkU~Z}lQ(J9JZYWEF)*a;j}9M_*_p zP~;e9pDvN6bXU>BCN4|baTSKwvRgb+aMuIAJY1VY;SD$c=3mi2Oez^7+0TXpojK0JA6N9j`DPruID%~FR=7S%nW4BA}(=DDn~8I}JB zyI9Z!*mrSO06lD*qJ`HoFNT>ogi(V^f1sh_Bv0KspggMQtJXWPX}W#dDkN&W4L7;L zsQIhnJW#08&8Dh+u01!{J>(x0ep}C^_U4-dlyW8xQlz_5X(%#FmU-kvqrFU+SJT4Z z4QEX+HqgXtCnkb)dalhG7;a`#S<)~~B*#-Gr?zI4pd{Z(O!N&Ky^GwAFOKIhm6Qrj zIp>Xa@n=%_DNhpntKHZX3`WpHI}SPyWIp1s(XcHgRQV~qz`N8FKY}AY!T(1!s^S&a zOS)ga<>Ymzllf!{rot^X`S@2|Tcn7rB8okl7)Mu4tNlC~mSpPM@s9EGa#1U*g;a#g z%U4qmN{&r3ZKEThhpFr!Kd?OF+F6&{k6_O-VZub&JRyAre6@~Dt3xE&Cal_b`y9?0 zb@j-H zfB2G%m)u%51WKmo6p{{q=PLVh&D5j##}u)38hLzS$F}-wwYNqXwMu3l%wDX2C=EK% zzB}!V^#^+!=k1O;;li{{{I|B>2^7HFnM`Qs5Er^;|AU?+u-{s70QzhxQ>>vW-Dv#I zN|^?l5warDQVV{e=G_scJH0a@WAm5Hhzh(TbctNk%wOSk{NI>*{wZu}*A4g9?Zz`{ zdjxtnQVFvmGGyDbTGBlO` zD!@B9{xT`&;Ic~te6``$-nmyKr+;P^EO#dIZ!p-YuLzyX-8INDi<9V5>n^^2hJ6?Q zzZT)<-i{DZX$GZ)@HcM&aC~Twp}LiV2~Ch!wU2cLT$V+T7?`-OX$=&?Nu5bq?Rj>D zLvYxpzvytS`?QBderi2K$jvh$#=~OQPlBXhX1p;W-wUa8_ucA4twA%K7iZAN%S-y& zN+$&9{8@;_{7Knf{az?7^(b45ybW#|p$NsKC5@(9ygOa4(vh@D5Yy&zge(cpHSgCk zj}5xV^P-73C^Q#BXL$eil7}W3{a+N>05{H6qgLV%CH{b2j&;#bPp0XD4Vgd@Ih|49 zSa&K!J5QPbx1PI5ioQyX@3e2{u(bmv8^x!2yk$aw4am8mU_5hiH8k2VJad3161gOn z#_N+ZLotolbeeM~r&FSm^+C5gWVceEg~2e2_(F-FagHD6vqO5cdH)Se_~OY4rz%p< zgMXtdq`qSN&3yCz1EjxbcR{Zi zh_JD_;4{n3e2qSbNe~7E=Z(C(!xh6-h##glX8?I<9a^mWCZJ7Cx#vqgkc)^_08nV& zqgmDk!Qv)jO{V=*p*cz#@?Xqja-1ty7VG+Uj|<;c(DR=!qf6%G(j(Fs(a*b?B}I7` zDl_RH?=ZsZ-X;<4;{OCc0g%y>aWie*AL)-22o2W=7YKbFA%#VNUxep^L5ub`_xJAo z#luI7?zp;V+m-YkEu*TB4mO!3Zw?4)oy4U2a7T_Y_bQYhEK*^$O36!&iT(tBDlIQ1 z{xJyrdSP@T;w}g3TPMdFYfz;Y@Uz!qH#yWt z^pZxjdasPERy^GQ9wRJ_7Y^^J{2)=pq+A!A&s*a3N=?dz-IG5wpL}6vAZ5)is~tQF|nH0#`3r>cTg6lEBXK? z$r&?U453K@Lm9}fbo!g+!2V$$Baw3#&ga4nMKFw`Cr@mjHupwU0flMy|K zGQK9_1nJJM`e@jbDDW)lCln;m7#Ldx6E{s+Hn96-sagjyTR935$$hu(UW2<< zX+7`pIP0hBWfs!rQZKy&F{4iS*-jP5#e%x8X-0RIdB)Sck?X{7C1)ZN!;2YDr;F+BgvkOe4Q{Ok# z&AZN8K_Nnxlg8#MXA0`9J5gGZF+gwMHrXfx{mrWD@A#H$?HGZQG#a{c&;9FHz#ekZo8rT{7RKXPcf zpBo{?(O%(o;s(L6#C!z!ZCl+~|ONXc|ytI;R=i=T>3iNsVg!aSG&5KV>PTgo47C4C> zLgW&@T-fwpc)yG}|9Ceo9sKqlF2PqSk1o>(&(Z1@FzNz>#D&Yye>{B9;Ez;={TB}O zYac?!{v_O|Yn%`fC7iIh4N;J=*Z*s5nXSgZtL7d zwQXeFn-Vym`;#L&iXGXt^Di8UQI>AvzT&=bfw)+|RoVgNvX}ek&%*vi5NBZ{ur2FR{UNzfW8v%;6i}b^~KTFr&ubQGeLl7 zf7^+BLr+JZlBxWE7`3D>VikuBKK1Oi3PbLSX7uTx^q1n1TN)=c#Q4X6b0Z-h?k{l= z`RAr|zr|)m2w)Q2|E1c7@f%t(81;pWUT3piCTOpCjYlluJ%Q*mg}x~bu%G(Xt&1c% z@AW2lRzbQRzm--1xyKvwJwy3r$BD%7;>w6@|%={+Eu&&CawT)Pv$4;i3tX* zP$5Uj1z|1j_nYvwQ@EawV5o>iJFMk%>unBf^mqM7&EDr0{(A2jmNlyMN2Vd1@SizA zp6ZtcHAZ)|V`NmcJEV5B0^&nBXk-5p4tQ}d<|nj+r=PQG*!z;4ZHH8?lPC66%#-(? zOZ45XS(9BPZ%l*Zp(3-KkgJwF%maiNe~PqF93YIQ3sI9}Jx)=rBtSNKsGT(1ux?qC zfb^10G@3Lx)Vb+{?vGRLq6$qsLr~gPuAfJ?+OqUU(ld?w7rD_?NrX8RfS{q+cw$TTvhAcKCk019+{aey=oF5zBOH(D;2@ zvL&an23mSLpgRkIxNBh9i%eC*8VjRKV4oRnwU~x~$jbk0%>2}0{sZeybY5h$+mGdx zhrF?m{4oDt21on{l_#N^hF$NrCDQp2{d-k4gU8mvF8%)O{f$?9K*=_bu=t2t@VXN% zqTj9k)2gRx-277qxqJQFw}dY6$)AfN@*qIy4Gz%yB-A=7wd2{(}mK zFG`S9!v7}=cI3^w Date: Thu, 19 Jun 2025 14:51:09 +0200 Subject: [PATCH 06/25] stablecoin tests and fungible roles --- packages/core/stellar/src/fungible.test.ts | 4 + packages/core/stellar/src/fungible.test.ts.md | 40 + .../core/stellar/src/fungible.test.ts.snap | Bin 1143 -> 1214 bytes packages/core/stellar/src/stablecoin.test.ts | 114 +++ .../core/stellar/src/stablecoin.test.ts.md | 709 ++++++++++++++++++ .../core/stellar/src/stablecoin.test.ts.snap | Bin 0 -> 1430 bytes 6 files changed, 867 insertions(+) create mode 100644 packages/core/stellar/src/stablecoin.test.ts create mode 100644 packages/core/stellar/src/stablecoin.test.ts.md create mode 100644 packages/core/stellar/src/stablecoin.test.ts.snap diff --git a/packages/core/stellar/src/fungible.test.ts b/packages/core/stellar/src/fungible.test.ts index 251efd2fc..c30994f70 100644 --- a/packages/core/stellar/src/fungible.test.ts +++ b/packages/core/stellar/src/fungible.test.ts @@ -67,6 +67,10 @@ testFungible('fungible ownable', { access: 'ownable', }); +testFungible('fungible roles', { + access: 'roles', +}); + testFungible('fungible full', { premint: '2000', access: 'ownable', diff --git a/packages/core/stellar/src/fungible.test.ts.md b/packages/core/stellar/src/fungible.test.ts.md index e00edb414..ea86ec515 100644 --- a/packages/core/stellar/src/fungible.test.ts.md +++ b/packages/core/stellar/src/fungible.test.ts.md @@ -358,6 +358,46 @@ Generated by [AVA](https://avajs.dev). impl Ownable for MyToken {}␊ ` +## fungible roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_access_control::{self as access_control, AccessControl};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, admin: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + access_control::set_admin(e, &admin);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + type ContractType = Base;␊ + ␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl AccessControl for MyToken {}␊ + ` + ## fungible full > Snapshot 1 diff --git a/packages/core/stellar/src/fungible.test.ts.snap b/packages/core/stellar/src/fungible.test.ts.snap index 642146586c222cb0b163f0ab8193c6c1f9e9c9ee..f14e0b607f0d981b9fe630f41ad9db3cba26f2e8 100644 GIT binary patch literal 1214 zcmV;v1VQ^jRzV?ee5$#fQ_+x$W&*oSU3^s2Ou zjpr%zzaNVT00000000B+Szl`;MG%h(Ucx*GzKUX*$Z`vlY?7zY;e@+fltYg@*a&J^ zhu)c<%aES#x$d4zLRiEn9|RwL66Aap^c(n%{02Tq&%d47naL%0ag%j>9+sKz>Z~W?9c9n#0H#fF7Hm$Xf4jFf(@&=X_O9C<_bfkBT(D{AO6GTcI z+-%+WEeIoO!#;aqLtlx2Jsa(&3{r`XM|>PpUC+f`f}pd{ zUUV}W{b&^sl0-QnQCNHCF0o;2C*LYUzTlxFmC)m>nUK|P+x)@jtKIHy!hU3pw5nxG z44PH1SO9!3CN`taB2xJCK!4mz6yLS1k(B}Nd2Ux2#)9Ey%>7lt+@JG{x!gtw<+gv2 zjTk(>As($>Pvd6)R1qcv&ZHi=n%vwMRnZMlk8muTp?+th0`(8?%q`R>;an!>Cs`Dn zOG16X60#8+o}@3$3vx9$qi1@Ih$>m`mlETA$cCrc+o*X>%3C6IazvQJxJqV-H-$j7 zA=wLp%v)0mO#Kv|&=L|YG+8Hkn`Zp#>9xZN(+`qipMesXNuQJqrZ-jC%Lc_uA2{SO zQwV~s?VYkI`-J)kmE|Ke=4?YpGqs_kc+R7+z$Nu_G>5U0G9t_?`StqM&IH=f$z)x) zi{g;{QUx?W9~=CbM@oYWeFo{?$4O-U6Bu$%OB#>EFsJQS?KqC#L51tsbS|c}B_bxc zC%aF{C*9Q`CT6cAs904Ob=la+&b5}*a{6AajJhDnEdO3fGQTX(ZIZc4J!KM*NkAq6 z-HrrQtJ1GrmA}Fu`e0~vR@+8XsHaPdeO%2Edhu1A@M?A1t!y&Wr&nvdY0$Owm<{F(=B^QQQGvPahSC7Je=3ms+d%HMuiEB@JH@|9%6Ee|u4`El zCxTNV>rt}wK_yDwS(@D_!LCcBbP{q-ONg#c*l<7j-b+7c{B*YgdttzCB623}tpz2@ zBmtDX%|ZZG(9jY_VG%}tOldueR(`5P(RVAe8%3t&dplab_51D(Cdu~u@AgIa-%ID$ zmVQx7BQG;ts!nkXx^CL1yJX|8*i5#tsaa>b+NP^*y4t3zJ*!>q)AuWN3#^07!-$eo zU^r~JGOzqmDPw*$Wz2k*F?+F8JiM@!DUsZKIg=^_@G+RO=D#az>Ml2>&8(L;b)TB@ c<~quox?N3)a}6a<#jGRiUo?}|IA1dW0Byci6aWAK literal 1143 zcmV--1c>`VRzVB!Wi|51n2Q_C*w9ZS=t@)yMESCj1)`$AnQ1 zeN7m{68b`lA?C0zxR%)03clUi+}Yf6);~Pr!c#gJI8LGns7%mP!3jbap9Fye$#FC0{;px}uf@e&5hH|(*x%1r zEFNDGk4~>=aC2~`30G8bWfo?$TjZi{x&i7jPE<40?`+nf{=uDvh596%tHRXIh2@<*-7{JOHRO=cE8WgC!fK(+zB z84YNPrO(XDUl0)UVrWfP+d(s^XIsm2%$5kVdDbMn*q!z$+YWzAI=mczJW3K#u1U}^ zDEXz<*neEyLCM^QAudSb)0h%diSuuRnRp1T0{Tgq94e&)2GjawTM-5zm_0JlO`OVUp#iTL& ziP9pvl*&{@Zl2C$&H&s6OKV;qt(i`_r8c*n+Dzxv(wpm`H`8&o6z3W!PA#k>=RXT% JE+-f;002gqFf#xE diff --git a/packages/core/stellar/src/stablecoin.test.ts b/packages/core/stellar/src/stablecoin.test.ts new file mode 100644 index 000000000..bb021098f --- /dev/null +++ b/packages/core/stellar/src/stablecoin.test.ts @@ -0,0 +1,114 @@ +import test from 'ava'; + +import type { StablecoinOptions } from './stablecoin'; +import { buildStablecoin } from './stablecoin'; +import { printContract } from './print'; + +import type { OptionsError } from '.'; +import { stablecoin } from '.'; + +function testStablecoin(title: string, opts: Partial) { + test(title, t => { + const c = buildStablecoin({ + name: 'MyStablecoin', + symbol: 'MST', + ...opts, + }); + t.snapshot(printContract(c)); + }); +} + +/** + * Tests external API for equivalence with internal API + */ +function testAPIEquivalence(title: string, opts?: StablecoinOptions) { + test(title, t => { + t.is( + stablecoin.print(opts), + printContract( + buildStablecoin({ + name: 'MyStablecoin', + symbol: 'MST', + ...opts, + }), + ), + ); + }); +} + +testStablecoin('basic stablecoin', {}); + +testStablecoin('stablecoin burnable', { + burnable: true, +}); + +testStablecoin('stablecoin pausable', { + pausable: true, +}); + +testStablecoin('stablecoin burnable pausable', { + burnable: true, + pausable: true, +}); + +testStablecoin('stablecoin preminted', { + premint: '1000', +}); + +testStablecoin('stablecoin premint of 0', { + premint: '0', +}); + +testStablecoin('stablecoin mintable', { + mintable: true, +}); + +testStablecoin('stablecoin ownable', { + access: 'ownable', +}); + +testStablecoin('stablecoin roles', { + access: 'roles', +}); + +testStablecoin('stablecoin allowlist', { + limitations: 'allowlist', +}); + +testStablecoin('stablecoin blocklist', { + limitations: 'blocklist', +}); + +testStablecoin('stablecoin full', { + premint: '2000', + access: 'ownable', + burnable: true, + mintable: true, + pausable: true, +}); + +testStablecoin('stablecoin full - complex name', { + name: 'Custom $ Token', + premint: '2000', + access: 'ownable', + burnable: true, + mintable: true, + pausable: true, +}); + +testAPIEquivalence('stablecoin API default'); + +testAPIEquivalence('stablecoin API basic', { name: 'CustomToken', symbol: 'CTK' }); + +testAPIEquivalence('stablecoin API full', { + name: 'CustomToken', + symbol: 'CTK', + premint: '2000', + burnable: true, + mintable: true, + pausable: true, +}); + +test('stablecoin API assert defaults', async t => { + t.is(stablecoin.print(stablecoin.defaults), stablecoin.print()); +}); diff --git a/packages/core/stellar/src/stablecoin.test.ts.md b/packages/core/stellar/src/stablecoin.test.ts.md new file mode 100644 index 000000000..a7533b528 --- /dev/null +++ b/packages/core/stellar/src/stablecoin.test.ts.md @@ -0,0 +1,709 @@ +# Snapshot report for `src/stablecoin.test.ts` + +The actual snapshot is saved in `stablecoin.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## basic stablecoin + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + }␊ + ` + +## stablecoin burnable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleBurnable for MyStablecoin {}␊ + ` + +## stablecoin pausable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + ownable::set_owner(e, &owner);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyStablecoin {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[only_owner]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin burnable pausable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + ownable::set_owner(e, &owner);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyStablecoin {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyStablecoin {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[only_owner]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin preminted + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, recipient: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + Base::mint(e, &recipient, 1000000000000000000000);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + }␊ + ` + +## stablecoin premint of 0 + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + }␊ + ` + +## stablecoin mintable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + }␊ + ` + +## stablecoin ownable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + ownable::set_owner(e, &owner);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_access_control::{self as access_control, AccessControl};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, admin: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + access_control::set_admin(e, &admin);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl AccessControl for MyStablecoin {}␊ + ` + +## stablecoin allowlist + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{allowlist::{AllowList, FungibleAllowList}, Base, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + ownable::set_owner(e, &owner);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = AllowList;␊ + ␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleAllowList for MyStablecoin {␊ + fn allowed(e: &Env, account: Address) -> bool {␊ + AllowList::allowed(e, &account)␊ + }␊ + ␊ + #[only_owner]␊ + fn allow_user(e: &Env, user: Address, operator: Address) {␊ + AllowList::allow_user(e, &user);␊ + }␊ + ␊ + #[only_owner]␊ + fn disallow_user(e: &Env, user: Address, operator: Address) {␊ + AllowList::disallow_user(e, &user);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin blocklist + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, blocklist::{BlockList, FungibleBlockList}, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + ownable::set_owner(e, &owner);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = BlockList;␊ + ␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBlockList for MyStablecoin {␊ + fn blocked(e: &Env, account: Address) -> bool {␊ + BlockList::blocked(e, &account)␊ + }␊ + ␊ + #[only_owner]␊ + fn block_user(e: &Env, user: Address, operator: Address) {␊ + BlockList::block_user(e, &user);␊ + }␊ + ␊ + #[only_owner]␊ + fn unblock_user(e: &Env, user: Address, operator: Address) {␊ + BlockList::unblock_user(e, &user);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin full + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + ownable::set_owner(e, &owner);␊ + }␊ + ␊ + #[only_owner]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128) {␊ + Base::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = Base;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyStablecoin {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyStablecoin {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[only_owner]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin full - complex name + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct CustomToken;␊ + ␊ + #[contractimpl]␊ + impl CustomToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "Custom $ Token"), String::from_str(e, "MST"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + ownable::set_owner(e, &owner);␊ + }␊ + ␊ + #[only_owner]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128) {␊ + Base::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for CustomToken {␊ + type ContractType = Base;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for CustomToken {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for CustomToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[only_owner]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for CustomToken {}␊ + ` diff --git a/packages/core/stellar/src/stablecoin.test.ts.snap b/packages/core/stellar/src/stablecoin.test.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..8acdb8738aaabef72c8270f574d7f8f0b00a702a GIT binary patch literal 1430 zcmV;H1!?+0RzVr00000000B+T2E^uHxy43@`t(gP)ebc*QB@!Ox8&bWl`9|ZkobEmR)cP zZG$_=Gn&m1SsF%~O+xUYAE2M0hq8O@q0mpzYfruQ>lDo&d1TKxaq8Hcc;;XA{0~QE|LvhNb`1!5pVTpfOJXu;;T~N;l z%L^Ya9xXPT(0cXF_uEH-OPCqSoMTUCQv$Lm{L z`w5Sp)l)(j71J(XgrN}dc~rJ6c^MLI<74&mBw50~W%aDg4MpDpp%X_;y_9(3zlJCN zJ-?oq>jCHoVGOz4AFJZGq7F zDPa!dGWmyiQ;ks@l2swd>^9AqRgX4|Vv~rXhBcDb)I(eSy>T*Nj&{=RGmsHx!Y2jQ zsg-HX*&uo01$TMOB!Xaf?{QJ7Ekb>Siu@7kQ?{X@h}zJQJg1@6a6|bVMWHXG2nq8F z7M{G=8ABTy8LyG6(D?bC3}_CWCC1F7r^=3gii*VRZ#UatLx*!(5W63r`PeN}nf<_S z8&v!Hb1|hA0Wv^6Q9(L<&}JoUVs;xwde$(jn?^|nc%!zSi%&~3uo0Wk>OaON^w;X# zZbIXvV)X`9Z&39H_5N;9Lka%v68dYjq7b9fVpXe6JwKhQ7c-tU)Z}q3alJUzs;<+3 zdzS{t;umN{x&f2KD=EbMZtVWQ&Tk=RhQ$^eBH{*NKvaCrXs=9pC^tzQQwwCn?oQb= zy9(p^uxA7JVW+Nlzn9MaP%DWgl|JQ)74`1g6KhYrV^53XOBUl{@PBJz41+X@)*weE+DbDA(h zdcFd$bn5kmx@3|Qi(=thy%p+~JR}j8yxa!{`DdhTXsE^wN_bu%CMd%IwLvlNFmoy5 zYH}~I0#_Rbu6{g^xSAdIbtf11=RZX#ND3?%^J6>Pl@(M zof6#`@+$zP;v_uNUMfx%yk2+mtrwF`EIaM4FNo9^MCuD7^#zfKbV20evw`FWw!!7y zkdg~vxLa~|wDyOQ>-<*dI`f(99K=HMZnD0rRHrax_L)v948RwV2vjFKcarQ3o4n3- k?t88?Y|}c`nMA5Htk*iznLwsvsI_PP2RD@erXoK80HKVy5C8xG literal 0 HcmV?d00001 From a309f6c70ea8ecb1bebbe113ae9e7b5eb26eff65 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:22:19 +0200 Subject: [PATCH 07/25] remove ContractOverride import --- .../core/stellar/src/non-fungible.test.ts.md | 16 +++++++--------- .../stellar/src/non-fungible.test.ts.snap | Bin 1583 -> 1558 bytes packages/core/stellar/src/non-fungible.ts | 2 -- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/core/stellar/src/non-fungible.test.ts.md b/packages/core/stellar/src/non-fungible.test.ts.md index 56b8c2b30..c851c8f86 100644 --- a/packages/core/stellar/src/non-fungible.test.ts.md +++ b/packages/core/stellar/src/non-fungible.test.ts.md @@ -299,7 +299,7 @@ Generated by [AVA](https://avajs.dev). use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ - Base, ContractOverrides, enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ + Base, enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ };␊ ␊ #[contract]␊ @@ -342,8 +342,7 @@ Generated by [AVA](https://avajs.dev). use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ - Base, consecutive::{NonFungibleConsecutive, Consecutive}, ContractOverrides,␊ - NonFungibleToken␊ + Base, consecutive::{NonFungibleConsecutive, Consecutive}, NonFungibleToken␊ };␊ use stellar_ownable::{self as ownable, Ownable};␊ use stellar_ownable_macro::only_owner;␊ @@ -402,7 +401,7 @@ Generated by [AVA](https://avajs.dev). use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ Base, burnable::NonFungibleBurnable, consecutive::{NonFungibleConsecutive, Consecutive},␊ - ContractOverrides, NonFungibleToken␊ + NonFungibleToken␊ };␊ use stellar_ownable::{self as ownable, Ownable};␊ use stellar_ownable_macro::only_owner;␊ @@ -464,8 +463,7 @@ Generated by [AVA](https://avajs.dev). use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ - Base, consecutive::{NonFungibleConsecutive, Consecutive}, ContractOverrides,␊ - NonFungibleToken␊ + Base, consecutive::{NonFungibleConsecutive, Consecutive}, NonFungibleToken␊ };␊ use stellar_ownable::{self as ownable, Ownable};␊ use stellar_ownable_macro::only_owner;␊ @@ -553,7 +551,7 @@ Generated by [AVA](https://avajs.dev). use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ Base, burnable::NonFungibleBurnable, consecutive::{NonFungibleConsecutive, Consecutive},␊ - ContractOverrides, NonFungibleToken␊ + NonFungibleToken␊ };␊ use stellar_ownable::{self as ownable, Ownable};␊ use stellar_ownable_macro::only_owner;␊ @@ -739,8 +737,8 @@ Generated by [AVA](https://avajs.dev). use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ use stellar_default_impl_macro::default_impl;␊ use stellar_non_fungible::{␊ - Base, burnable::NonFungibleBurnable, ContractOverrides,␊ - enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ + Base, burnable::NonFungibleBurnable, enumerable::{NonFungibleEnumerable, Enumerable},␊ + NonFungibleToken␊ };␊ use stellar_ownable::{self as ownable, Ownable};␊ use stellar_ownable_macro::only_owner;␊ diff --git a/packages/core/stellar/src/non-fungible.test.ts.snap b/packages/core/stellar/src/non-fungible.test.ts.snap index ca49c83e13603377ee0fe4cf2a7fc9dc2fe94af9..0c9d675f8c677f36815d280aa4c5d92b595f0f7c 100644 GIT binary patch literal 1558 zcmV+x2I=`hRzVXqifcNTfniag4ygcH3opf*sbqzz#d_vh!YJZ?Fd#s6Q5IOHQ1+u4B^&L!$V7 zB)^Z}k3ar<7Z6G|y_g*N%>etHpokw+!GCk-q>75HlY<$yx}Z$7twq!Pc=2?zh|Rt*|4c;w_hk z_cyjRNrFOLC7RWzUxatUCJicu4!%?cq>UitSU4dTNOm8YD`vi(6m4hPGKsF$m9|km z&?vW6ztw?9S)Tz#C&In1bRb`zB-Portge;y_gL-epcylQO1u2hr&^bPxHQ*YKDQHu ziW{C~i6$a$W<*%En(VdpbAbu>Dd93<phftAx z!L12Efen(jK#)0YR-r-udajp3NwcNITJkhu1d*Rt5BuERNRmAQ1Ve;-SaFgxM9)Vy z5LA0P!Nl=f5X6rkss7u?fd`>l0SF7#HdvEdZLlV2p`>SrD^}5wI*NMo2$Po5o!&sr zvl4BvmKAnMLwaESQuslR(u6jTl8sKToQ&mGou1T)ysmT1= zsM6L@1(P_$tYX>R(QsvNUYE_Syj$3cxgfkyyj$<3%Ua~m-9a?6%yRk^FQ*r-8{?vS zp|on*;e9P6r9-hB3;(Qatp7)g$-m}xtXE=Ev6mi{JH-bZC@&RZX;{LPhyR#ZLK6x1 z*crkVP)K7Us+hv&Q~5zL@s8U6ROK0aNY!mj2Emld427`aB`R9_G3!lGlJk32061h* zfB6#)2{)Dv5?-?YdJUjVlpC-#U}?beT3|V@0~ixV-RcdmmH#x<{ddKn?zHnyvllkA zpk2DhV6fcm;@yB?6@y(vixE zhr{q`>@)13SR5d69NIGgCLU!jPK}VM826&g0}ap;&~Xb8qLGlKc@W&J zA2e_~wdNGuuD%>I>fyf`T#GTlJo~_0F^{R3t&Vu~TD5Lni6SnFSto6*|TFf7`JI$iA z;vNlR@gIu_00000000B+Tu*Q0G!!rGcK?JE7led_yxXN}QTj*A&1Dy{rAXDXbftDB zpc*1i;uY%}>Zt&l~+L&hc?(Dr#9FZEFedntxQ4IqT^v7C82O+JtsmgqY$cPdTm09 zQb?p!4NF(kki$?j{JpKfP@9$Vg7~?`iw>n7iqPVZ^osDas7b?Gp@S!?fV2^W91ADH0?FYcYb7kO)28iQTQ1SDI?^_( z2O8D3>a{xXDeEzyIE!(2C>_X~hiP}VEURPX<2_MFI%p=0pvob?@ToTBHy37l$QO2k zP;tZKywF6%^^6E>V?&PGowmS)2b6GGFm%(!D9~H4iMJRfygJ@){;9>=?>A>U-YNo0 zm7%RX8qYZneSkQ~OKh;0T~rGcy`dyLlVzCc)e840ii;u}Jj<^-)iRlG4q@vU6NgZd zKEd7?puh&{yFidTZCas0{<^N0Lg{NuiRJWa%m^YMFCF%{yO9?A1PF!*_p#z6X^5_m zY#^w1bApKzcn~BXKT!R*k3%0qwF3~As%@|=wc21=&{9d)4wvkrBXyMhyX{hwM1c0LMs&2{>IU1VQ z(t0im!VtoS6QW8U>1o@1H3lG>t>p|?>4M|r1v^VXz|1wyte%R@ zuZ$`!4OK8nBFt*i=1jvCcUIHpRxvE>Zjbj)F=K&;xvf|b!gIypJKb{GvEsVZ4^WoL zuupJ?y>#6O^X#S4x=h4-T9itMVmA@NY1Q=quNJ2N%C_6qoEJ_-XL-O87ad(;H(3 z;sLv5Z)-sO-z@{g*OaN6{k5sZ^2&V+1My~O@EYt44!0UHn_LsH$<_Yo!WG>BMsep? zjZyq~$1sZNT-q7JVF-sI9ENaA7~wESkw$zJ$uPXBV|e<31CC!Ll9Hj&e41)LO`V!! z0==K6_H5uM@exBFt~tN=n>Or!n$->bH~P7p;vop1mp8jaU`9|_Tf4iWP=vO!!N!79 zyrLAZHq0VpI=>pIU>~X0ih)#3JRF8!Bmu(?O2o0^%osByD1%_R!l7LQVB%Be;>-xS zic#lj?rVUSf{t5)5RHW-&HdnJ{i1=}57vx=+vPJfHQPHQIcIo%(=!IEryr|pmNqpD z*dY&ntJd97VoAnnxhQeo=)rI8u<78nyGj3;^v^V=e@1nXdcygb$xKZ^W~%+Z#%b45 zLL1_CI*C;rUlJOX#pYxGS^wC7JK=&x1L>erWnw8|)<*#DgNdbvuSC51ePI3pz|_#L he&-tMHJ*P0&~JIeQ%1m3G_!WB{{bj41C8ZX003t81_1y7 diff --git a/packages/core/stellar/src/non-fungible.ts b/packages/core/stellar/src/non-fungible.ts index e14e4cd6d..3776ab4a4 100644 --- a/packages/core/stellar/src/non-fungible.ts +++ b/packages/core/stellar/src/non-fungible.ts @@ -185,7 +185,6 @@ function addBurnable(c: ContractBuilder, pausable: boolean) { function addEnumerable(c: ContractBuilder) { c.addUseClause('stellar_non_fungible', 'enumerable::{NonFungibleEnumerable, Enumerable}'); c.addUseClause('stellar_default_impl_macro', 'default_impl'); - c.addUseClause('stellar_non_fungible', 'ContractOverrides'); const nonFungibleEnumerableTrait = { traitName: 'NonFungibleEnumerable', @@ -200,7 +199,6 @@ function addEnumerable(c: ContractBuilder) { function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) { c.addUseClause('stellar_non_fungible', 'consecutive::{NonFungibleConsecutive, Consecutive}'); - c.addUseClause('stellar_non_fungible', 'ContractOverrides'); const nonFungibleConsecutiveTrait = { traitName: 'NonFungibleConsecutive', From 11e6da92efb1729367889f6a2d11191515c1da70 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:29:55 +0200 Subject: [PATCH 08/25] clean --- packages/core/stellar/src/generate/stablecoin.ts | 3 ++- packages/core/stellar/src/set-access-control.ts | 2 -- packages/core/stellar/src/stablecoin.ts | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/core/stellar/src/generate/stablecoin.ts b/packages/core/stellar/src/generate/stablecoin.ts index 461be1cd0..42717eff5 100644 --- a/packages/core/stellar/src/generate/stablecoin.ts +++ b/packages/core/stellar/src/generate/stablecoin.ts @@ -1,4 +1,4 @@ -import type { StablecoinOptions } from '../stablecoin'; +import { limitationsOptions, type StablecoinOptions } from '../stablecoin'; import { accessOptions } from '../set-access-control'; import { infoOptions } from '../set-info'; import { generateAlternatives } from './alternatives'; @@ -14,6 +14,7 @@ const blueprint = { mintable: booleans, premint: ['1'], access: accessOptions, + limitations: limitationsOptions, info: infoOptions, }; diff --git a/packages/core/stellar/src/set-access-control.ts b/packages/core/stellar/src/set-access-control.ts index 9dd45848f..e5df2dd43 100644 --- a/packages/core/stellar/src/set-access-control.ts +++ b/packages/core/stellar/src/set-access-control.ts @@ -4,8 +4,6 @@ export const accessOptions = [false, 'ownable', 'roles'] as const; export const DEFAULT_ACCESS_CONTROL = 'ownable'; export type Access = (typeof accessOptions)[number]; -export type OwnableProps = { useMacro: boolean }; -export type RolesProps = { useMacro: boolean; caller: string; role: string }; export type AccessProps = { useMacro: boolean; caller?: string; diff --git a/packages/core/stellar/src/stablecoin.ts b/packages/core/stellar/src/stablecoin.ts index f861dba1d..28a7df7ee 100644 --- a/packages/core/stellar/src/stablecoin.ts +++ b/packages/core/stellar/src/stablecoin.ts @@ -18,6 +18,8 @@ export const defaults: Required = { limitations: false, } as const; +export const limitationsOptions = [false, 'allowlist', 'blocklist'] as const; + export function printStablecoin(opts: StablecoinOptions = defaults): string { return printContract(buildStablecoin(opts)); } From 1fd464ecdf0f71df28bd0cbf4c6e8e2a66755eb0 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:37:46 +0200 Subject: [PATCH 09/25] version rc --- packages/core/stellar/src/contract.test.ts.md | 16 +++++----- .../core/stellar/src/contract.test.ts.snap | Bin 583 -> 589 bytes packages/core/stellar/src/fungible.test.ts.md | 22 +++++++------- .../core/stellar/src/fungible.test.ts.snap | Bin 1214 -> 1217 bytes .../core/stellar/src/non-fungible.test.ts.md | 28 +++++++++--------- .../stellar/src/non-fungible.test.ts.snap | Bin 1558 -> 1561 bytes packages/core/stellar/src/stablecoin.test.ts | 1 - .../core/stellar/src/stablecoin.test.ts.md | 26 ++++++++-------- .../core/stellar/src/stablecoin.test.ts.snap | Bin 1430 -> 1434 bytes packages/core/stellar/src/utils/version.ts | 4 +-- 10 files changed, 48 insertions(+), 49 deletions(-) diff --git a/packages/core/stellar/src/contract.test.ts.md b/packages/core/stellar/src/contract.test.ts.md index a9b02bc0f..5de36f693 100644 --- a/packages/core/stellar/src/contract.test.ts.md +++ b/packages/core/stellar/src/contract.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ #[contract]␊ @@ -21,7 +21,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ #[contract]␊ @@ -40,7 +40,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ #[contract]␊ @@ -59,7 +59,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ #[contract]␊ @@ -81,7 +81,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ #[contract]␊ @@ -103,7 +103,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use some::library::SomeLibrary;␊ @@ -117,7 +117,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use some::library::{Misc, SomeLibrary};␊ @@ -131,7 +131,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use another::library::{self as custom1, self as custom2, AnotherLibrary};␊ diff --git a/packages/core/stellar/src/contract.test.ts.snap b/packages/core/stellar/src/contract.test.ts.snap index b1432072a44eb96a57212054356256e4da451809..aafb641592048ac0c340f457be75e4d249a2a5f9 100644 GIT binary patch literal 589 zcmV-T0L?Jq# zpPLisd)D1E6jd46nD_<^`!M8df&jUhRI*(7Uagl>D?*EctlAi&ms$wLR`f9 zUBddgW>_#i&Tl6$TiQU0i@Kt4~Yr9#z-Q9q|w?i!J;}>C=V%8B9NRSU>R#~ zZ!TW3w{zN^gP_O-9sC2@P*T!S@^k1ZDA7G^e6|IaB83#W;-sCOyWQ`beVj*uCi&WZ z_>8PrBS|uBg=7eXQ_?ACMB+xo?)`Zf3Kqwo)@rwE*;Bt+yM5zht=)#+^PS_?fs$w( zw)R6btRjWhhr|6Nuib`iiW9*q&}UP*9zcZ!e?Cdm|8WI`*z`gc Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -39,7 +39,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -77,7 +77,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -146,7 +146,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -232,7 +232,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -263,7 +263,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -293,7 +293,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -323,7 +323,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -363,7 +363,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -403,7 +403,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -496,7 +496,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ diff --git a/packages/core/stellar/src/fungible.test.ts.snap b/packages/core/stellar/src/fungible.test.ts.snap index f14e0b607f0d981b9fe630f41ad9db3cba26f2e8..6abdf3b40e6256968e686f5384f6ee1237ae4f84 100644 GIT binary patch literal 1217 zcmV;y1U~ygRzV}}Y?Eh&r|kmDqMDHyasn`J<;7A@cm z*cNyKEm4gSiquG|Nf7v9z}~jEJ?<&PKEQzO8|)kH6AaWpOSY88OXkEg^b*JP#}U_43P=rPU?<-1}hZ z!{sN--7fUreD+o6W#AGfN#}(}mm8dXgVjvuHXoj$Ewu1T@X5djwP|- z^K9UN9}_m*wXBW9Tyo!v;sHn{VpqYdvxK5TpxO5={hLq2xdi~A*ZB+_$K@d} zRm+y>*Q;K!0Qg*tZAP6%q;Tqi{&R zgJ`rN*$aZqTQiDI{S=jl(h1_mg3tffATWpOg%yH&xin2E|Js zIOH)?2!idM$7NIY2=x&v%SUL$*@l*8YC}u$oJV1SE9&QH4kIOHM3`6d>&>g33ACY= z$+~nG#Yy?43TS>v*7z}xlo}WM4AQ-iGD+tt3^=DHjYnaa)3&R29K~XJ;e`gTi^qWs+=ngG ze7pF5u6fP6DsSKQ2A(O&O-&eE|Jnn$T=;+x;kOQv*dd({haf2-UjT20lSUx8Mn6< zlqi!#QSvqqQB*-gLo9_wEcr2|lcBWsb0w61Sex@uG7aIo(GZ@z2;XA5Y`z%pTy`2b>rS(jSpqQ;rDtEqEtpw6k7b!hzqLR*QBmNNhVT-jU$ literal 1214 zcmV;v1VQ^jRzV?ee5$#fQ_+x$W&*oSU3^s2Ou zjpr%zzaNVT00000000B+Szl`;MG%h(Ucx*GzKUX*$Z`vlY?7zY;e@+fltYg@*a&J^ zhu)c<%aES#x$d4zLRiEn9|RwL66Aap^c(n%{02Tq&%d47naL%0ag%j>9+sKz>Z~W?9c9n#0H#fF7Hm$Xf4jFf(@&=X_O9C<_bfkBT(D{AO6GTcI z+-%+WEeIoO!#;aqLtlx2Jsa(&3{r`XM|>PpUC+f`f}pd{ zUUV}W{b&^sl0-QnQCNHCF0o;2C*LYUzTlxFmC)m>nUK|P+x)@jtKIHy!hU3pw5nxG z44PH1SO9!3CN`taB2xJCK!4mz6yLS1k(B}Nd2Ux2#)9Ey%>7lt+@JG{x!gtw<+gv2 zjTk(>As($>Pvd6)R1qcv&ZHi=n%vwMRnZMlk8muTp?+th0`(8?%q`R>;an!>Cs`Dn zOG16X60#8+o}@3$3vx9$qi1@Ih$>m`mlETA$cCrc+o*X>%3C6IazvQJxJqV-H-$j7 zA=wLp%v)0mO#Kv|&=L|YG+8Hkn`Zp#>9xZN(+`qipMesXNuQJqrZ-jC%Lc_uA2{SO zQwV~s?VYkI`-J)kmE|Ke=4?YpGqs_kc+R7+z$Nu_G>5U0G9t_?`StqM&IH=f$z)x) zi{g;{QUx?W9~=CbM@oYWeFo{?$4O-U6Bu$%OB#>EFsJQS?KqC#L51tsbS|c}B_bxc zC%aF{C*9Q`CT6cAs904Ob=la+&b5}*a{6AajJhDnEdO3fGQTX(ZIZc4J!KM*NkAq6 z-HrrQtJ1GrmA}Fu`e0~vR@+8XsHaPdeO%2Edhu1A@M?A1t!y&Wr&nvdY0$Owm<{F(=B^QQQGvPahSC7Je=3ms+d%HMuiEB@JH@|9%6Ee|u4`El zCxTNV>rt}wK_yDwS(@D_!LCcBbP{q-ONg#c*l<7j-b+7c{B*YgdttzCB623}tpz2@ zBmtDX%|ZZG(9jY_VG%}tOldueR(`5P(RVAe8%3t&dplab_51D(Cdu~u@AgIa-%ID$ zmVQx7BQG;ts!nkXx^CL1yJX|8*i5#tsaa>b+NP^*y4t3zJ*!>q)AuWN3#^07!-$eo zU^r~JGOzqmDPw*$Wz2k*F?+F8JiM@!DUsZKIg=^_@G+RO=D#az>Ml2>&8(L;b)TB@ c<~quox?N3)a}6a<#jGRiUo?}|IA1dW0Byci6aWAK diff --git a/packages/core/stellar/src/non-fungible.test.ts.md b/packages/core/stellar/src/non-fungible.test.ts.md index c851c8f86..9a6640761 100644 --- a/packages/core/stellar/src/non-fungible.test.ts.md +++ b/packages/core/stellar/src/non-fungible.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -42,7 +42,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -83,7 +83,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -155,7 +155,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -244,7 +244,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -293,7 +293,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -336,7 +336,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -394,7 +394,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -457,7 +457,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -544,7 +544,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -645,7 +645,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -678,7 +678,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -731,7 +731,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -842,7 +842,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ diff --git a/packages/core/stellar/src/non-fungible.test.ts.snap b/packages/core/stellar/src/non-fungible.test.ts.snap index 0c9d675f8c677f36815d280aa4c5d92b595f0f7c..5b2b17660e51eb8c1fe8b30d2c8eb7b1643b5bed 100644 GIT binary patch literal 1561 zcmV+!2Il!eRzVHWcrYrfGHCZ9sv6ZHgc%jF=pB`VP(kt#{W2?7t;VaM&TZ;%1|20QG$%g*~6`v$uW)E|qqB_~c@*Kz2BAyIsf zNS{`l|AZ-vx7*EVe?0R%B+#3gzh`gE z-;lqL=5BmE^XJU!D%gJg;+w`-fr|;pjV%uo5%>XS5WL*lu~t{X2907Qq(0CN!~u8} zWAZJIV;lwqYzrKQh=Fa&XbTarL5X0<6&!r`XywVuqXu(V9$O3d_6T*j@S2vDa13~s z+u`0Jgr}c*9>bj5z|Dorryz<$8*Gvz8*B>}kp0eDrXVZU@vx7QP&hK56CsyT2-P$_ zH=)ESYy;uxbHuR?zNX~!JZ*;_V$xZ&tcAUz0!=GUT7V0dxB|R9Npol!WGPKc{uiIY zsRaN4QqCv9aa>9=dCHbB1dE&GXgSX#e<2niVF6grTL_`gXykCgmaq-(x7+QN=tM}x zD=v-hFRy8m1Vy+`G_Oyu2tSEhG^`cc`BD{-HiD32;Y3&<*@tAVgavk5w4H0qB|26| z+D7$2v)Wd@RtG+1Jq8pfG4A%K9r?1JR%gw!I#%B46Scd8R>BCX?Dw-Tw0{3|*7WDdY&DT<3E8@$e+I@L0nZVqAl027B$ zk$%D681TRbX}`F9O)B~HNPB18_z(C(om^$2>?ajW!;n|ax^uq zrS)7ChBdL+qdjeZmc7?w6~!dvG6i*|`ir_~kmQJ3Gsqo(T1hPj40pHRXv5tXx6N=j zJjXPnp&1R$Xn5U6!@l{;W#%jAm_=gK0IvH&sqp;H z(9+hR1(PJgtY#_Q)4*l-S}&zrMc=R$n?ZQ0xOlUhE^ARdclsg8GE3|eyu@C*ZiEZ% zrP8`ph)=Y*ln%viB7&o;QU4z;F#o#NqrMWDiqZ7Q+$cZTKzXT&O~V%^90Nwg7g|WT z2hIT2fFha@QO6k?&*TTi#2ae=Q|t+_lBEon4PK#*r_^xcs*WY`~v}YGSiT4@uaLw7fGp+Cc zdaZZiKj@`5#Z3_ImM>dGU`9|_o4Y%=P=wa99>;E#i-Y49%!hRB9B`l5sihU&;1Bz z{iGq>k#(IB?&7O6!=4YE!lfC5&eM<1HPf1!$?K3uv{mbFD)A(PvYe1OAM}{Ec2IQi zyxlDQn5Ca-T>2T+A?OKbU}jZn0#>Dt?`kZzky6+YAE$#@#U=d{jl6zuF6o;~`mO*Q z2^TaP$O)A!6Uz&;Is)(jOe{ZqC-&9v19NeIYPeUwa}EC*&*lAo%Nw#X0XqifcNTfniag4ygcH3opf*sbqzz#d_vh!YJZ?Fd#s6Q5IOHQ1+u4B^&L!$V7 zB)^Z}k3ar<7Z6G|y_g*N%>etHpokw+!GCk-q>75HlY<$yx}Z$7twq!Pc=2?zh|Rt*|4c;w_hk z_cyjRNrFOLC7RWzUxatUCJicu4!%?cq>UitSU4dTNOm8YD`vi(6m4hPGKsF$m9|km z&?vW6ztw?9S)Tz#C&In1bRb`zB-Portge;y_gL-epcylQO1u2hr&^bPxHQ*YKDQHu ziW{C~i6$a$W<*%En(VdpbAbu>Dd93<phftAx z!L12Efen(jK#)0YR-r-udajp3NwcNITJkhu1d*Rt5BuERNRmAQ1Ve;-SaFgxM9)Vy z5LA0P!Nl=f5X6rkss7u?fd`>l0SF7#HdvEdZLlV2p`>SrD^}5wI*NMo2$Po5o!&sr zvl4BvmKAnMLwaESQuslR(u6jTl8sKToQ&mGou1T)ysmT1= zsM6L@1(P_$tYX>R(QsvNUYE_Syj$3cxgfkyyj$<3%Ua~m-9a?6%yRk^FQ*r-8{?vS zp|on*;e9P6r9-hB3;(Qatp7)g$-m}xtXE=Ev6mi{JH-bZC@&RZX;{LPhyR#ZLK6x1 z*crkVP)K7Us+hv&Q~5zL@s8U6ROK0aNY!mj2Emld427`aB`R9_G3!lGlJk32061h* zfB6#)2{)Dv5?-?YdJUjVlpC-#U}?beT3|V@0~ixV-RcdmmH#x<{ddKn?zHnyvllkA zpk2DhV6fcm;@yB?6@y(vixE zhr{q`>@)13SR5d69NIGgCLU!jPK}VM826&g0}ap;&~Xb8qLGlKc@W&J zA2e_~wdNGuuD%>I>fyf`T#GTlJo~_0F^{R3t&Vu~TD5Lni6SnFSto6*|TF) { diff --git a/packages/core/stellar/src/stablecoin.test.ts.md b/packages/core/stellar/src/stablecoin.test.ts.md index a7533b528..84977c3db 100644 --- a/packages/core/stellar/src/stablecoin.test.ts.md +++ b/packages/core/stellar/src/stablecoin.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -39,7 +39,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -77,7 +77,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -146,7 +146,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -232,7 +232,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -263,7 +263,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -293,7 +293,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -323,7 +323,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -363,7 +363,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -403,7 +403,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -465,7 +465,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -527,7 +527,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ @@ -620,7 +620,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ #![no_std]␊ ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ diff --git a/packages/core/stellar/src/stablecoin.test.ts.snap b/packages/core/stellar/src/stablecoin.test.ts.snap index 8acdb8738aaabef72c8270f574d7f8f0b00a702a..dbbe338a88f42f50f3a8692fd640f4e00da92ed8 100644 GIT binary patch literal 1434 zcmV;L1!ej{RzVFkr_W2J92;y3?-vFa!06mTbw1Q^#&%>0nSa`QGFE z^W)Pa-??4N?Gy3yuS9y1lAw!)XM>QqONq@r<_U%aam=N7`_6H&#NRLew6w6Ypq`JG z7d}~hwb*Jw`{k4Gn$J9&FhQD69m1sNdL%^f{OO_5YQaAD11!BRC2;D=V|Wn|_5%q5 zLOlj;Nhrl3w0X$8n87}0GQ_qN@Z;9zmz!J7(B9lJ);~L9+!E617)B%rh-9=SoD+mD z9y?A*gfPKQC-+;PA5aqx*c%htGW6K%-flu5VQV?W#SxX38fW>~4mm>kzT#*%Vz1RY z5W3jIf|&44+HuHF25Ht?hn~ijgdXz8-2_h|@NURL?keIOjC|fi_ZpeL0$WZmK3l~ONl%F zS8&IF=G+}KMIn^gVK42eUG)ZB)o8VpLmiw+!i2|}P#xnj&+JpezcdsA9EtK!xc90M z3VZkFI}`@pJwKSArcrVs2z3DqNPA3pku=IDWMXhe&s84@i^DSGxT;#DC??KaK8RgX4|ViSv^hBe~W)Z<(Iy?!)cj!xX|GLRu= z!X-J?sg-HXnIL)Y1^YZ=5`nk9^C+*>HlZ#;dHx9XDVxwxL``T&p3%^1xT1WPqR5v*od8I<=?_i z^!Lhq??mGyW%Vvq?^5+H_2KSPL#h7tQu|A^q7b9fVpXe6JU^ML7c-tU)Z}q3ak)6v zs;+Z^TbB#S{I_UGe2+=ul+yKQD{iqwae&3GTN7Xf54r01DyEd5QcuX?P+M*mLhZrwEf2M8 zM~T*v;;-7zJR$pGqq}-0O!u6%-U6q*1!_}|yy`H5qb4w-P8Yjdzffl?kV@)_TKB`H zJ53lNJzpVMGWGH@UOdT)M7}Jp-f~q<9*_`AUhV^f{8LgUG*sgX1wO|U6O>_q+MpOW zn8W13HNHt$0j^&Z!1eyzf@^vnm}yG&sCE%zbyu$j*E{<9-FIRGgTn+DpW#!r04B$n|21Y4y)S!t-05@XT?-vmXh`{dj#<`A%-g?9-h@7=X_q7O2j6ZY1Lw oHhG=!-1dZL*rs*9Gl_g>Sg&=uGl6ucpw^!8KPFPDD`P(Z0K_E9v;Y7A literal 1430 zcmV;H1!?+0RzVr00000000B+T2E^uHxy43@`t(gP)ebc*QB@!Ox8&bWl`9|ZkobEmR)cP zZG$_=Gn&m1SsF%~O+xUYAE2M0hq8O@q0mpzYfruQ>lDo&d1TKxaq8Hcc;;XA{0~QE|LvhNb`1!5pVTpfOJXu;;T~N;l z%L^Ya9xXPT(0cXF_uEH-OPCqSoMTUCQv$Lm{L z`w5Sp)l)(j71J(XgrN}dc~rJ6c^MLI<74&mBw50~W%aDg4MpDpp%X_;y_9(3zlJCN zJ-?oq>jCHoVGOz4AFJZGq7F zDPa!dGWmyiQ;ks@l2swd>^9AqRgX4|Vv~rXhBcDb)I(eSy>T*Nj&{=RGmsHx!Y2jQ zsg-HX*&uo01$TMOB!Xaf?{QJ7Ekb>Siu@7kQ?{X@h}zJQJg1@6a6|bVMWHXG2nq8F z7M{G=8ABTy8LyG6(D?bC3}_CWCC1F7r^=3gii*VRZ#UatLx*!(5W63r`PeN}nf<_S z8&v!Hb1|hA0Wv^6Q9(L<&}JoUVs;xwde$(jn?^|nc%!zSi%&~3uo0Wk>OaON^w;X# zZbIXvV)X`9Z&39H_5N;9Lka%v68dYjq7b9fVpXe6JwKhQ7c-tU)Z}q3alJUzs;<+3 zdzS{t;umN{x&f2KD=EbMZtVWQ&Tk=RhQ$^eBH{*NKvaCrXs=9pC^tzQQwwCn?oQb= zy9(p^uxA7JVW+Nlzn9MaP%DWgl|JQ)74`1g6KhYrV^53XOBUl{@PBJz41+X@)*weE+DbDA(h zdcFd$bn5kmx@3|Qi(=thy%p+~JR}j8yxa!{`DdhTXsE^wN_bu%CMd%IwLvlNFmoy5 zYH}~I0#_Rbu6{g^xSAdIbtf11=RZX#ND3?%^J6>Pl@(M zof6#`@+$zP;v_uNUMfx%yk2+mtrwF`EIaM4FNo9^MCuD7^#zfKbV20evw`FWw!!7y zkdg~vxLa~|wDyOQ>-<*dI`f(99K=HMZnD0rRHrax_L)v948RwV2vjFKcarQ3o4n3- k?t88?Y|}c`nMA5Htk*iznLwsvsI_PP2RD@erXoK80HKVy5C8xG diff --git a/packages/core/stellar/src/utils/version.ts b/packages/core/stellar/src/utils/version.ts index 323307f99..b7597d34b 100644 --- a/packages/core/stellar/src/utils/version.ts +++ b/packages/core/stellar/src/utils/version.ts @@ -1,13 +1,13 @@ /** * The actual latest version to use in links. */ -export const contractsVersion = '0.2.0'; +export const contractsVersion = '0.3.0-rc.2'; export const contractsVersionTag = `v${contractsVersion}`; /** * Semantic version string representing of the minimum compatible version of Contracts to display in output. */ -export const compatibleContractsSemver = '^0.2.0'; +export const compatibleContractsSemver = '^0.3.0-rc.2'; /** * The Soroban version for which compilation and testing have passing tests From 72c00dd0b6bdace404421cd42aaa11514d5c80ab Mon Sep 17 00:00:00 2001 From: Boyan Barakov <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:43:28 +0200 Subject: [PATCH 10/25] Update packages/core/stellar/src/contract.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/core/stellar/src/contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/stellar/src/contract.ts b/packages/core/stellar/src/contract.ts index f6da7e091..2fc655356 100644 --- a/packages/core/stellar/src/contract.ts +++ b/packages/core/stellar/src/contract.ts @@ -247,7 +247,7 @@ export class ContractBuilder implements Contract { addConstructorCode(code: string): void { for (const existingCode of this.constructorCode) { - if (existingCode == code) { + if (existingCode === code) { return; } } From 080c79133d5d38c6f2cebea2bf4963df47ee5a11 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Thu, 19 Jun 2025 16:24:53 +0200 Subject: [PATCH 11/25] api version and fix typo --- packages/core/stellar/package.json | 2 +- packages/ui/src/stellar/App.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/stellar/package.json b/packages/core/stellar/package.json index 1f6f23f52..bf893d84b 100644 --- a/packages/core/stellar/package.json +++ b/packages/core/stellar/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/wizard-stellar", - "version": "0.2.1", + "version": "0.3.0", "description": "A boilerplate generator to get started with OpenZeppelin Stellar Soroban Contracts", "license": "AGPL-3.0-only", "repository": "https://github.com/OpenZeppelin/contracts-wizard", diff --git a/packages/ui/src/stellar/App.svelte b/packages/ui/src/stellar/App.svelte index 22d2bf37a..c5be04cd9 100644 --- a/packages/ui/src/stellar/App.svelte +++ b/packages/ui/src/stellar/App.svelte @@ -48,7 +48,7 @@ const getButtonVisibilities = (opts?: KindedOptions[Kind]): ButtonVisibilities => { return { - downloadScaffold: opts?.kind === 'Fungible' || opts?.kind === 'NonFungible' || opts?.kind === 'Stableocoin' ? true : false, + downloadScaffold: opts?.kind === 'Fungible' || opts?.kind === 'NonFungible' || opts?.kind === 'Stablecoin' ? true : false, }; }; From f094f05d20c3f6be3cad90f29a1cd1c0320a3049 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:35:45 +0200 Subject: [PATCH 12/25] fix ai configs --- .../function-definitions/stellar-shared.ts | 10 +++-- .../function-definitions/stellar.ts | 39 ++++++++++++++++++- .../ui/api/ai-assistant/types/languages.ts | 10 ++--- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts b/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts index 67a87a095..a8ff231ea 100644 --- a/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts @@ -3,10 +3,12 @@ import type { StellarCommonContractOptions } from '../types/languages.ts'; export const stellarCommonFunctionDescription = { access: { - type: 'boolean', - enum: [false], - description: 'The type of access control to provision, currently not supported', - // 'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions.', + anyOf: [ + { type: 'string', enum: ['ownable', 'roles'] }, + { type: 'boolean', enum: [false] }, + ], + description: + 'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions. Roles is a flexible mechanism with a separate role for each privileged action. A role can have many authorized accounts.', }, info: { diff --git a/packages/ui/api/ai-assistant/function-definitions/stellar.ts b/packages/ui/api/ai-assistant/function-definitions/stellar.ts index 232cbc311..777589968 100644 --- a/packages/ui/api/ai-assistant/function-definitions/stellar.ts +++ b/packages/ui/api/ai-assistant/function-definitions/stellar.ts @@ -31,9 +31,46 @@ export const stellarFungibleAIFunctionDefinition = { }, } as const satisfies AiFunctionDefinition<'stellar', 'Fungible'>; +export const stellarStablecoinAIFunctionDefinition = { + name: 'Stablecoin', + description: 'Make a stablecoin that uses Fungible Token Standard, compatible with SEP-41.', + parameters: { + type: 'object', + properties: { + ...addFunctionPropertiesFrom(stellarCommonFunctionDescription, [ + 'name', + 'symbol', + 'burnable', + 'pausable', + 'mintable', + 'access', + 'info', + ]), + limitations: { + anyOf: [ + { type: 'boolean', enum: [false] }, + { type: 'string', enum: ['allowlist', 'blocklist'] }, + ], + description: + 'Whether to restrict certain users from transferring tokens, either via allowing or blocking them.', + }, + premint: { + type: 'string', + description: 'The number of tokens to premint for the deployer.', + }, + upgradeable: { + type: 'boolean', + description: 'Whether the contract can be upgraded.', + }, + }, + required: ['name', 'symbol'], + additionalProperties: false, + }, +} as const satisfies AiFunctionDefinition<'stellar', 'Stablecoin'>; + export const stellarNonFungibleAIFunctionDefinition = { name: 'NonFungible', - description: 'Non-Fungible Token Standard, compatible with SEP-50, similar to ERC-721', + description: 'Non-Fungible Token Standard, similar to ERC-721', parameters: { type: 'object', properties: { diff --git a/packages/ui/api/ai-assistant/types/languages.ts b/packages/ui/api/ai-assistant/types/languages.ts index e264b63a1..d5e03f36e 100644 --- a/packages/ui/api/ai-assistant/types/languages.ts +++ b/packages/ui/api/ai-assistant/types/languages.ts @@ -11,8 +11,7 @@ export type { CommonContractOptions as CairoAlphaCommonContractOptions } from '. export type { RoyaltyInfoOptions as CairoAlphaRoyaltyInfoOptions } from '../../../../core/cairo_alpha/dist/set-royalty-info'; //Stellar import type { KindedOptions as StellarKindedOptions } from '../../../../core/stellar/dist'; -import type { CommonContractOptions as StellarCommonContractOptionsBase } from '../../../../core/stellar/dist/common-options'; -export type StellarCommonContractOptions = Omit & { access?: false }; +export type { CommonContractOptions as StellarCommonContractOptions } from '../../../../core/stellar/dist/common-options'; // Stylus import type { KindedOptions as StylusKindedOptions } from '../../../../core/stylus/dist'; import type { CommonContractOptions as StylusCommonContractOptionsBase } from '../../../../core/stylus/dist/common-options'; @@ -27,9 +26,10 @@ export type LanguagesContractsOptions = { }; cairo: CairoKindedOptions; cairoAlpha: CairoAlphaKindedOptions; - stellar: Omit & { - Fungible: StellarKindedOptions['Fungible'] & StellarCommonContractOptions; - NonFungible: StellarKindedOptions['NonFungible'] & StellarCommonContractOptions; + stellar: Omit & { + Fungible: StellarKindedOptions['Fungible']; + NonFungible: StellarKindedOptions['NonFungible']; + Stablecoin: StellarKindedOptions['Stablecoin']; }; stylus: Omit & { ERC20: StylusKindedOptions['ERC20'] & StylusCommonContractOptions; From 27307595d3c063723e97fd4abd7c56a8d53e9523 Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:39:23 +0200 Subject: [PATCH 13/25] changeset --- .changeset/ninety-readers-kneel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ninety-readers-kneel.md diff --git a/.changeset/ninety-readers-kneel.md b/.changeset/ninety-readers-kneel.md new file mode 100644 index 000000000..121645aa0 --- /dev/null +++ b/.changeset/ninety-readers-kneel.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard-stellar': minor +--- + +Add Stablecoin with Limitations and Access Control (ownable and roles) From 15051c408bbc0532b3bf2f1340de6ed79a99f22f Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:03:52 +0200 Subject: [PATCH 14/25] prettier --- packages/ui/src/stellar/App.svelte | 3 ++- packages/ui/src/stellar/FungibleControls.svelte | 2 +- packages/ui/src/stellar/NonFungibleControls.svelte | 2 +- packages/ui/src/stellar/StablecoinControls.svelte | 4 +--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/stellar/App.svelte b/packages/ui/src/stellar/App.svelte index c5be04cd9..875361e4c 100644 --- a/packages/ui/src/stellar/App.svelte +++ b/packages/ui/src/stellar/App.svelte @@ -48,7 +48,8 @@ const getButtonVisibilities = (opts?: KindedOptions[Kind]): ButtonVisibilities => { return { - downloadScaffold: opts?.kind === 'Fungible' || opts?.kind === 'NonFungible' || opts?.kind === 'Stablecoin' ? true : false, + downloadScaffold: + opts?.kind === 'Fungible' || opts?.kind === 'NonFungible' || opts?.kind === 'Stablecoin' ? true : false, }; }; diff --git a/packages/ui/src/stellar/FungibleControls.svelte b/packages/ui/src/stellar/FungibleControls.svelte index 76cbe907e..049766612 100644 --- a/packages/ui/src/stellar/FungibleControls.svelte +++ b/packages/ui/src/stellar/FungibleControls.svelte @@ -76,6 +76,6 @@
- + diff --git a/packages/ui/src/stellar/NonFungibleControls.svelte b/packages/ui/src/stellar/NonFungibleControls.svelte index 40138c9e6..322e6845e 100644 --- a/packages/ui/src/stellar/NonFungibleControls.svelte +++ b/packages/ui/src/stellar/NonFungibleControls.svelte @@ -99,6 +99,6 @@ }} /> - + diff --git a/packages/ui/src/stellar/StablecoinControls.svelte b/packages/ui/src/stellar/StablecoinControls.svelte index 2af17b718..392c37a46 100644 --- a/packages/ui/src/stellar/StablecoinControls.svelte +++ b/packages/ui/src/stellar/StablecoinControls.svelte @@ -77,7 +77,6 @@ - - - + From 5576e65e5470729521a932aee5a894579eeb28e6 Mon Sep 17 00:00:00 2001 From: Boyan Barakov <9572072+brozorec@users.noreply.github.com> Date: Tue, 24 Jun 2025 16:55:31 +0200 Subject: [PATCH 15/25] Update .changeset/ninety-readers-kneel.md Co-authored-by: Eric Lau --- .changeset/ninety-readers-kneel.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.changeset/ninety-readers-kneel.md b/.changeset/ninety-readers-kneel.md index 121645aa0..acc1f281f 100644 --- a/.changeset/ninety-readers-kneel.md +++ b/.changeset/ninety-readers-kneel.md @@ -2,4 +2,6 @@ '@openzeppelin/wizard-stellar': minor --- -Add Stablecoin with Limitations and Access Control (ownable and roles) +Add Stablecoin with Limitations and Access Control (ownable and roles). +- **Breaking changes**: + - Use OpenZeppelin Stellar Soroban Contracts v0.3.0-rc.2 From 2219d8bf8a2e35d13d58326cc431ab9a52715a1d Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 24 Jun 2025 12:07:05 -0400 Subject: [PATCH 16/25] Use overridable implicit name function --- packages/core/stellar/src/contract.ts | 2 +- .../core/stellar/src/utils/define-functions.ts | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/core/stellar/src/contract.ts b/packages/core/stellar/src/contract.ts index 2fc655356..675d28114 100644 --- a/packages/core/stellar/src/contract.ts +++ b/packages/core/stellar/src/contract.ts @@ -46,9 +46,9 @@ export interface TraitImplBlock extends BaseTraitImplBlock { } export interface BaseFunction { + name: string; args: Argument[]; code: string[]; - name?: string; pub?: boolean; returns?: string; } diff --git a/packages/core/stellar/src/utils/define-functions.ts b/packages/core/stellar/src/utils/define-functions.ts index 3eb15e5df..ac37dca52 100644 --- a/packages/core/stellar/src/utils/define-functions.ts +++ b/packages/core/stellar/src/utils/define-functions.ts @@ -1,7 +1,16 @@ import type { BaseFunction } from '../contract'; -export function defineFunctions(fns: Record): Record; +type OverridableImplicitNameFunction = Omit & { name?: string }; -export function defineFunctions(fns: Record): Record { - return Object.fromEntries(Object.entries(fns).map(([name, fn]) => [name, Object.assign({ name }, fn)])); +export function defineFunctions( + fns: Record, +): Record; + +export function defineFunctions(fns: Record): Record { + return Object.fromEntries( + Object.entries(fns).map(([implicitName, fn]) => [ + implicitName, + Object.assign({ name: fn.name ?? implicitName }, fn), + ]), + ); } From c610a33b768117bb2f67c3e6a2a7b71915fd1acf Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Wed, 25 Jun 2025 10:25:40 +0200 Subject: [PATCH 17/25] suggestions --- packages/core/stellar/src/fungible.test.ts | 10 +- packages/core/stellar/src/fungible.test.ts.md | 97 ++++- .../core/stellar/src/fungible.test.ts.snap | Bin 1217 -> 1371 bytes packages/core/stellar/src/fungible.ts | 39 +- packages/core/stellar/src/stablecoin.test.ts | 30 +- .../core/stellar/src/stablecoin.test.ts.md | 381 +++++++++++++++++- .../core/stellar/src/stablecoin.test.ts.snap | Bin 1434 -> 1789 bytes packages/core/stellar/src/stablecoin.ts | 5 +- 8 files changed, 540 insertions(+), 22 deletions(-) diff --git a/packages/core/stellar/src/fungible.test.ts b/packages/core/stellar/src/fungible.test.ts index c30994f70..8d105f82d 100644 --- a/packages/core/stellar/src/fungible.test.ts +++ b/packages/core/stellar/src/fungible.test.ts @@ -71,7 +71,7 @@ testFungible('fungible roles', { access: 'roles', }); -testFungible('fungible full', { +testFungible('fungible full - ownable', { premint: '2000', access: 'ownable', burnable: true, @@ -79,6 +79,14 @@ testFungible('fungible full', { pausable: true, }); +testFungible('fungible full - roles', { + premint: '2000', + access: 'roles', + burnable: true, + mintable: true, + pausable: true, +}); + testFungible('fungible full - complex name', { name: 'Custom $ Token', premint: '2000', diff --git a/packages/core/stellar/src/fungible.test.ts.md b/packages/core/stellar/src/fungible.test.ts.md index b69825f3d..bc37c3f33 100644 --- a/packages/core/stellar/src/fungible.test.ts.md +++ b/packages/core/stellar/src/fungible.test.ts.md @@ -398,7 +398,7 @@ Generated by [AVA](https://avajs.dev). impl AccessControl for MyToken {}␊ ` -## fungible full +## fungible full - ownable > Snapshot 1 @@ -491,6 +491,101 @@ Generated by [AVA](https://avajs.dev). impl Ownable for MyToken {}␊ ` +## fungible full - roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ + use stellar_access_control::{self as access_control, AccessControl};␊ + use stellar_access_control_macros::has_role;␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, admin: Address, pauser: Address, minter: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + access_control::set_admin(e, &admin);␊ + access_control::grant_role_no_auth(e, &admin, &pauser, &Symbol::new(e, "pauser"));␊ + access_control::grant_role_no_auth(e, &admin, &minter, &Symbol::new(e, "minter"));␊ + }␊ + ␊ + #[has_role(caller, "minter")]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128, caller: Address) {␊ + Base::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + type ContractType = Base;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[has_role(caller, "pauser")]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[has_role(caller, "pauser")]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl AccessControl for MyToken {}␊ + ` + ## fungible full - complex name > Snapshot 1 diff --git a/packages/core/stellar/src/fungible.test.ts.snap b/packages/core/stellar/src/fungible.test.ts.snap index 6abdf3b40e6256968e686f5384f6ee1237ae4f84..a650e40da6f53829857d94402ae728b2c3f825e9 100644 GIT binary patch literal 1371 zcmV-h1*G~xRzVwr@3|xrk{pkpQM~o|s)k zZFAdN#2$u)yEWzgn1EoRiPJ zkLEs}e>UH4L+9-m->kjzY(hC%d+87=Jl7);g4ZwijCLD#SP)_%`N6R#4&hBm=yxOx ziSJS92;%!Vf)0yV4^!A-R7BVo9KPRJf3m)@7TN1t#`2?m$}BFNu3^NS09Vu&ca9J` zd+s<9;oJl}T|9X6g3vc%m%cNhBO;F;^tTlQCDw9?i(_9{lFkaS9WjK8V`b{hE(o2y zz?_)yWje6OjtK2<8^-c}Cb?^baSynNVq3uL(}bc#AlY{f`I}9{nE?PG*SQod%Vt!q zXVD5l(AuT%R#O`JNEHy`L|6e4Sh{D0n6UXY+bTkC!~%*M|YlTy4tzJIsl zeK@9z#Oydtg)>fk7chr(#Dq8MrG7!C2BZF|93xSgEOSnQaW-Va+w`qpzee%q2pu01 zYEdSV8RASK8%;>|f*|$Ql(JJkxhJHAL<>pQO5UcK#BzFhf5`ORWZ0!3B22kU3I>y# z%I#%>V1*AHu$T%2-saZRqA5GXcM&SeM`*y=gqCD#LQAlWM{a>J^|K_0fs#BV)G7FN z@@l67O=zXEM(!d%DZdt;pB<7le#|nZ#)U2gwf8|KS^FG%jQItP2Wgnmu9odM$lq>> z>%eq2_Wg#87~-DnUXc&Gt42&r|Bs-2RaMluxsjf04fW;Z(@H}%qS7q>UaBm+sFmFo--D8lBa&Q5EWHX}*ukIYKVJtP?I)r`gK7%+E-f zj|SJshy)%L#HqneekoP=_jepOnL0LNLPTsY^oUHy>DNNVUIs@Jhq*2$Y;M#&*Mk4W z!+{LUg$)fp-?Ui1T{vF_jFXYq06y!$(}6eXzzg%hOD`xjm``|3sw|aK<&%XOPZez2gmWt)XMQ29%M&I%Pri55=Zv58CSWJ< z=yjyeu)U?AM42RulDApNqEa+8S^>_qu(0+*68=FqTlgw z7t?Stc=%7Lhxz;Ato1M^bWVd_rMI8R1))rv_XLNSThg32qI)(R-l}8Q#bOM)m@(M@^qJX3%k7P`H~%SAGGTmL+rJ6f#V=rC!Y;BgEk97 zpPT^2K^?C3N2w?LP2*a37}wf~xnRM_uCG9H{rHwr2H-Jhoa@7NKvkFPE@;-fpsG)G dCp3vpsA^Y@c})QGDw(x!{0D?T#Ph5@008`xuKfT2 literal 1217 zcmV;y1U~ygRzV}}Y?Eh&r|kmDqMDHyasn`J<;7A@cm z*cNyKEm4gSiquG|Nf7v9z}~jEJ?<&PKEQzO8|)kH6AaWpOSY88OXkEg^b*JP#}U_43P=rPU?<-1}hZ z!{sN--7fUreD+o6W#AGfN#}(}mm8dXgVjvuHXoj$Ewu1T@X5djwP|- z^K9UN9}_m*wXBW9Tyo!v;sHn{VpqYdvxK5TpxO5={hLq2xdi~A*ZB+_$K@d} zRm+y>*Q;K!0Qg*tZAP6%q;Tqi{&R zgJ`rN*$aZqTQiDI{S=jl(h1_mg3tffATWpOg%yH&xin2E|Js zIOH)?2!idM$7NIY2=x&v%SUL$*@l*8YC}u$oJV1SE9&QH4kIOHM3`6d>&>g33ACY= z$+~nG#Yy?43TS>v*7z}xlo}WM4AQ-iGD+tt3^=DHjYnaa)3&R29K~XJ;e`gTi^qWs+=ngG ze7pF5u6fP6DsSKQ2A(O&O-&eE|Jnn$T=;+x;kOQv*dd({haf2-UjT20lSUx8Mn6< zlqi!#QSvqqQB*-gLo9_wEcr2|lcBWsb0w61Sex@uG7aIo(GZ@z2;XA5Y`z%pTy`2b>rS(jSpqQ;rDtEqEtpw6k7b!hzqLR*QBmNNhVT-jU$ diff --git a/packages/core/stellar/src/fungible.ts b/packages/core/stellar/src/fungible.ts index c7c144e0d..21a507661 100644 --- a/packages/core/stellar/src/fungible.ts +++ b/packages/core/stellar/src/fungible.ts @@ -122,25 +122,36 @@ function addBase(c: ContractBuilder, name: string, symbol: string, pausable: boo } function addMintable(c: ContractBuilder, access: Access, pausable: boolean) { - if (access === 'ownable') { - c.addFreeFunction(functions.mint); + switch (access) { + case false: + break; + case 'ownable': { + c.addFreeFunction(functions.mint); - requireAccessControl(c, undefined, functions.mint, access); + requireAccessControl(c, undefined, functions.mint, access); - if (pausable) { - c.addFunctionTag(functions.mint, 'when_not_paused'); + if (pausable) { + c.addFunctionTag(functions.mint, 'when_not_paused'); + } + break; } - } else if (access === 'roles') { - c.addFreeFunction(functions.mint_with_caller); + case 'roles': { + c.addFreeFunction(functions.mint_with_caller); - requireAccessControl(c, undefined, functions.mint_with_caller, access, { - useMacro: true, - caller: 'caller', - role: 'minter', - }); + requireAccessControl(c, undefined, functions.mint_with_caller, access, { + useMacro: true, + caller: 'caller', + role: 'minter', + }); - if (pausable) { - c.addFunctionTag(functions.mint_with_caller, 'when_not_paused'); + if (pausable) { + c.addFunctionTag(functions.mint_with_caller, 'when_not_paused'); + } + break; + } + default: { + const _: never = access; + throw new Error('Unknown value for `access`'); } } } diff --git a/packages/core/stellar/src/stablecoin.test.ts b/packages/core/stellar/src/stablecoin.test.ts index a993fb6cf..96feae9ff 100644 --- a/packages/core/stellar/src/stablecoin.test.ts +++ b/packages/core/stellar/src/stablecoin.test.ts @@ -78,9 +78,37 @@ testStablecoin('stablecoin blocklist', { limitations: 'blocklist', }); -testStablecoin('stablecoin full', { +testStablecoin('stablecoin full - ownable, allowlist', { premint: '2000', access: 'ownable', + limitations: 'allowlist', + burnable: true, + mintable: true, + pausable: true, +}); + +testStablecoin('stablecoin full - ownable, blocklist', { + premint: '2000', + access: 'ownable', + limitations: 'blocklist', + burnable: true, + mintable: true, + pausable: true, +}); + +testStablecoin('stablecoin full - roles, allowlist', { + premint: '2000', + access: 'roles', + limitations: 'allowlist', + burnable: true, + mintable: true, + pausable: true, +}); + +testStablecoin('stablecoin full - roles, blocklist', { + premint: '2000', + access: 'roles', + limitations: 'blocklist', burnable: true, mintable: true, pausable: true, diff --git a/packages/core/stellar/src/stablecoin.test.ts.md b/packages/core/stellar/src/stablecoin.test.ts.md index 84977c3db..27657823b 100644 --- a/packages/core/stellar/src/stablecoin.test.ts.md +++ b/packages/core/stellar/src/stablecoin.test.ts.md @@ -522,7 +522,7 @@ Generated by [AVA](https://avajs.dev). impl Ownable for MyStablecoin {}␊ ` -## stablecoin full +## stablecoin full - ownable, allowlist > Snapshot 1 @@ -532,7 +532,9 @@ Generated by [AVA](https://avajs.dev). ␊ use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ use stellar_default_impl_macro::default_impl;␊ - use stellar_fungible::{Base, burnable::FungibleBurnable, FungibleToken};␊ + use stellar_fungible::{␊ + allowlist::{AllowList, FungibleAllowList}, Base, burnable::FungibleBurnable, FungibleToken␊ + };␊ use stellar_ownable::{self as ownable, Ownable};␊ use stellar_ownable_macro::only_owner;␊ use stellar_pausable::{self as pausable, Pausable};␊ @@ -559,7 +561,7 @@ Generated by [AVA](https://avajs.dev). #[default_impl]␊ #[contractimpl]␊ impl FungibleToken for MyStablecoin {␊ - type ContractType = Base;␊ + type ContractType = AllowList;␊ ␊ #[when_not_paused]␊ fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ @@ -577,6 +579,23 @@ Generated by [AVA](https://avajs.dev). //␊ ␊ #[contractimpl]␊ + impl FungibleAllowList for MyStablecoin {␊ + fn allowed(e: &Env, account: Address) -> bool {␊ + AllowList::allowed(e, &account)␊ + }␊ + ␊ + #[only_owner]␊ + fn allow_user(e: &Env, user: Address, operator: Address) {␊ + AllowList::allow_user(e, &user);␊ + }␊ + ␊ + #[only_owner]␊ + fn disallow_user(e: &Env, user: Address, operator: Address) {␊ + AllowList::disallow_user(e, &user);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ impl FungibleBurnable for MyStablecoin {␊ #[when_not_paused]␊ fn burn(e: &Env, from: Address, amount: i128) {␊ @@ -615,6 +634,362 @@ Generated by [AVA](https://avajs.dev). impl Ownable for MyStablecoin {}␊ ` +## stablecoin full - ownable, blocklist + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{␊ + Base, blocklist::{BlockList, FungibleBlockList}, burnable::FungibleBurnable, FungibleToken␊ + };␊ + use stellar_ownable::{self as ownable, Ownable};␊ + use stellar_ownable_macro::only_owner;␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + ownable::set_owner(e, &owner);␊ + }␊ + ␊ + #[only_owner]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128) {␊ + Base::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = BlockList;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBlockList for MyStablecoin {␊ + fn blocked(e: &Env, account: Address) -> bool {␊ + BlockList::blocked(e, &account)␊ + }␊ + ␊ + #[only_owner]␊ + fn block_user(e: &Env, user: Address, operator: Address) {␊ + BlockList::block_user(e, &user);␊ + }␊ + ␊ + #[only_owner]␊ + fn unblock_user(e: &Env, user: Address, operator: Address) {␊ + BlockList::unblock_user(e, &user);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyStablecoin {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyStablecoin {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[only_owner]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin full - roles, allowlist + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ + use stellar_access_control::{self as access_control, AccessControl};␊ + use stellar_access_control_macros::has_role;␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{␊ + allowlist::{AllowList, FungibleAllowList}, Base, burnable::FungibleBurnable, FungibleToken␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(␊ + e: &Env,␊ + recipient: Address,␊ + admin: Address,␊ + pauser: Address,␊ + minter: Address,␊ + manager: Address,␊ + ) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + access_control::set_admin(e, &admin);␊ + access_control::grant_role_no_auth(e, &admin, &pauser, &Symbol::new(e, "pauser"));␊ + access_control::grant_role_no_auth(e, &admin, &minter, &Symbol::new(e, "minter"));␊ + access_control::grant_role_no_auth(e, &admin, &manager, &Symbol::new(e, "manager"));␊ + }␊ + ␊ + #[has_role(caller, "minter")]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128, caller: Address) {␊ + Base::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = AllowList;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleAllowList for MyStablecoin {␊ + fn allowed(e: &Env, account: Address) -> bool {␊ + AllowList::allowed(e, &account)␊ + }␊ + ␊ + #[has_role(operator, "manager")]␊ + fn allow_user(e: &Env, user: Address, operator: Address) {␊ + AllowList::allow_user(e, &user);␊ + }␊ + ␊ + #[has_role(operator, "manager")]␊ + fn disallow_user(e: &Env, user: Address, operator: Address) {␊ + AllowList::disallow_user(e, &user);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyStablecoin {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyStablecoin {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[has_role(caller, "pauser")]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[has_role(caller, "pauser")]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl AccessControl for MyStablecoin {}␊ + ` + +## stablecoin full - roles, blocklist + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ + #![no_std]␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ + use stellar_access_control::{self as access_control, AccessControl};␊ + use stellar_access_control_macros::has_role;␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{␊ + Base, blocklist::{BlockList, FungibleBlockList}, burnable::FungibleBurnable, FungibleToken␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + #[contract]␊ + pub struct MyStablecoin;␊ + ␊ + #[contractimpl]␊ + impl MyStablecoin {␊ + pub fn __constructor(␊ + e: &Env,␊ + recipient: Address,␊ + admin: Address,␊ + pauser: Address,␊ + minter: Address,␊ + manager: Address,␊ + ) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyStablecoin"), String::from_str(e, "MST"));␊ + Base::mint(e, &recipient, 2000000000000000000000);␊ + access_control::set_admin(e, &admin);␊ + access_control::grant_role_no_auth(e, &admin, &pauser, &Symbol::new(e, "pauser"));␊ + access_control::grant_role_no_auth(e, &admin, &minter, &Symbol::new(e, "minter"));␊ + access_control::grant_role_no_auth(e, &admin, &manager, &Symbol::new(e, "manager"));␊ + }␊ + ␊ + #[has_role(caller, "minter")]␊ + #[when_not_paused]␊ + pub fn mint(e: &Env, account: Address, amount: i128, caller: Address) {␊ + Base::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyStablecoin {␊ + type ContractType = BlockList;␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBlockList for MyStablecoin {␊ + fn blocked(e: &Env, account: Address) -> bool {␊ + BlockList::blocked(e, &account)␊ + }␊ + ␊ + #[has_role(operator, "manager")]␊ + fn block_user(e: &Env, user: Address, operator: Address) {␊ + BlockList::block_user(e, &user);␊ + }␊ + ␊ + #[has_role(operator, "manager")]␊ + fn unblock_user(e: &Env, user: Address, operator: Address) {␊ + BlockList::unblock_user(e, &user);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyStablecoin {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyStablecoin {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + #[has_role(caller, "pauser")]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + #[has_role(caller, "pauser")]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl AccessControl for MyStablecoin {}␊ + ` + ## stablecoin full - complex name > Snapshot 1 diff --git a/packages/core/stellar/src/stablecoin.test.ts.snap b/packages/core/stellar/src/stablecoin.test.ts.snap index dbbe338a88f42f50f3a8692fd640f4e00da92ed8..a28cf4cd78ca1d33ad16f32e3b312b280f75f600 100644 GIT binary patch literal 1789 zcmV4b4*aoxd4Us3H zBma0h6d#KS00000000B+98GWBM6|T(hfFVAQH369wQ7XZq~%gg1X3G{RH;>4ZIFN} zm|1(ASoO|Wo^g{X+5?Cm0760>DFSiiz+d3Xi7P(_jX!2RYp=7LZ1y8Nd#LR3%zJO% z*Sz=UHE;Y*$lO=_`(KF&L`b3z=79@b;7&+f7SMoG9PvFS_&0An4-5Ro#jh`2xOzeU z-nx9@or^CnuCGJ;`6r*RJqcVwIazz`5h?;dATffckGHM$b=YFv2#cT-64(#K9z2T( z{gOlx2?GjkLBbHn&}K2~Ux_%t8bX0HhCZCcjNmj&h>D@r=RMVz<-o*uRpQ?*$V$Brfc)#kvF z1ppu+_!Jz+Wi*4t;@bp4>k)mun$M9xNen`q2&YQ~mV&xXY`A;B*fc_Z%(@O2u^eA1 zV_I1qHmSY6vbwsN;^?v>$W8hBccSoEnhxLx3FnhJuBBm-q%9t zBr%mQHR|}ErjGwCxjGh%La30#!+gZ3>KUl2wZ1MD>d}E9ln0D*IWQjbLOwP4YfT}- z39mPWD=#!tcz9*GO<_>o%ahrDZY4*Ygg#&n`G^hAvWNNw1sRNnhjNU>`m{ngCBVgy z4bStZUj3R3Hb-cGk5Gp)k$xfGR1MXJbXN#+xlPk>s^wi6AWeXN@om>lr3TKXZ-5>z_GmA47++ zutav>K8v}lIy3u*y`w?x^UuX$IK)5(pr ziNzXDqO1REC(++mm-{3dCnz(glsTo$DK)>RR3+3uS!jO@SER=nDzRa^%``t-sx~v8 zH00uOC2>4C!&z-&frX0&L^&6YNjIQ^cr|sIKeZ6xmnE;u%&_)iLrmNt3Wzk)86B7z z4yh}tb}E8wxVusJn_bm$zpCEAeAqCB@cGs7^IBc3ss2fQ98w53>S)w)8tNFA)G<$l zs@izzT}>O`zGSrVR9U`Bu4j{6*JdP>38bTv9mOmpyvB+)H7l|g-HNd563(4;Itxo0 zuYa)NLHfRxy`Gzc!3OMg13ELy8f3r3mQs`&So*Xu2BvAqsQD@mL$)6VTvSc%@|tF9 z?_FMSQ#-MhC>}}t^7%=?Mc-`HP|t?x-m{t`aLPxZ5_My%Ljy(yprKBes$0KMXGy7$LP@da!Ki@nyVpk(2OpSzNxAs+ue!F&3;|2L|!yploQ#hY<>VUce_v z!vM5FGR`oDDLdD+lW>T0{XlcBHKW&HZk?+V@=2Ym0?^=G zQ$-usxhlX2srBPH*L2Y-9NQrNv7Ku|CrE-0Y>KY+;8w}E`bikVTHn4K0$J-v&DVZs zEvv6hMKpFaJ94jvWtufke*ZMSQHl>)PY+jNw(>r8(hnA~%s4=E(s2^BAXff@#%N^5>T|_W3t%+$( zOnd4wjfrXJHKsMSu=%KknTXazv`ZGzu2du1w3(-|Mtl7aEuQ`P`l7|NHnb1Bol0xY zMEZO5xhmY-!`zXaUCiq6;zqp6vs++wVV=7X`cPMjkBKG>rY-nZtw4s&RBSMtyryIyVG`4>o13HsP4e0E{sX%8k zt%IF?DfKt!=R#rrw=-W}B4g5NNaOi3FL{ZnwJ$yDuqa3t0E)Xcf8*fVUs`_i z+qFf@Z_HmgoLn-pm2knj>Hdc0BBd#_Z!yY<0k{PzL8i;-OybFkr_W2J92;y3?-vFa!06mTbw1Q^#&%>0nSa`QGFE z^W)Pa-??4N?Gy3yuS9y1lAw!)XM>QqONq@r<_U%aam=N7`_6H&#NRLew6w6Ypq`JG z7d}~hwb*Jw`{k4Gn$J9&FhQD69m1sNdL%^f{OO_5YQaAD11!BRC2;D=V|Wn|_5%q5 zLOlj;Nhrl3w0X$8n87}0GQ_qN@Z;9zmz!J7(B9lJ);~L9+!E617)B%rh-9=SoD+mD z9y?A*gfPKQC-+;PA5aqx*c%htGW6K%-flu5VQV?W#SxX38fW>~4mm>kzT#*%Vz1RY z5W3jIf|&44+HuHF25Ht?hn~ijgdXz8-2_h|@NURL?keIOjC|fi_ZpeL0$WZmK3l~ONl%F zS8&IF=G+}KMIn^gVK42eUG)ZB)o8VpLmiw+!i2|}P#xnj&+JpezcdsA9EtK!xc90M z3VZkFI}`@pJwKSArcrVs2z3DqNPA3pku=IDWMXhe&s84@i^DSGxT;#DC??KaK8RgX4|ViSv^hBe~W)Z<(Iy?!)cj!xX|GLRu= z!X-J?sg-HXnIL)Y1^YZ=5`nk9^C+*>HlZ#;dHx9XDVxwxL``T&p3%^1xT1WPqR5v*od8I<=?_i z^!Lhq??mGyW%Vvq?^5+H_2KSPL#h7tQu|A^q7b9fVpXe6JU^ML7c-tU)Z}q3ak)6v zs;+Z^TbB#S{I_UGe2+=ul+yKQD{iqwae&3GTN7Xf54r01DyEd5QcuX?P+M*mLhZrwEf2M8 zM~T*v;;-7zJR$pGqq}-0O!u6%-U6q*1!_}|yy`H5qb4w-P8Yjdzffl?kV@)_TKB`H zJ53lNJzpVMGWGH@UOdT)M7}Jp-f~q<9*_`AUhV^f{8LgUG*sgX1wO|U6O>_q+MpOW zn8W13HNHt$0j^&Z!1eyzf@^vnm}yG&sCE%zbyu$j*E{<9-FIRGgTn+DpW#!r04B$n|21Y4y)S!t-05@XT?-vmXh`{dj#<`A%-g?9-h@7=X_q7O2j6ZY1Lw oHhG=!-1dZL*rs*9Gl_g>Sg&=uGl6ucpw^!8KPFPDD`P(Z0K_E9v;Y7A diff --git a/packages/core/stellar/src/stablecoin.ts b/packages/core/stellar/src/stablecoin.ts index 28a7df7ee..dab64b990 100644 --- a/packages/core/stellar/src/stablecoin.ts +++ b/packages/core/stellar/src/stablecoin.ts @@ -8,6 +8,7 @@ import { defaults as fungibleDefaults, withDefaults as withFungibleDefaults, functions as fungibleFunctions, + isAccessControlRequired as fungibleIsAccessControlRequired, } from './fungible'; import { requireAccessControl, type Access } from './set-access-control'; @@ -38,7 +39,7 @@ function withDefaults(opts: StablecoinOptions): Required { } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable === true || opts.limitations !== false || opts.pausable === true || opts.upgradeable === true; + return fungibleIsAccessControlRequired(opts) || opts.limitations !== false; } export function buildStablecoin(opts: StablecoinOptions): Contract { @@ -53,7 +54,7 @@ export function buildStablecoin(opts: StablecoinOptions): Contract { return c; } -function addLimitations(c: ContractBuilder, access: Access, mode: boolean | 'allowlist' | 'blocklist') { +function addLimitations(c: ContractBuilder, access: Access, mode: 'allowlist' | 'blocklist') { const type = mode === 'allowlist'; const limitationsTrait = { From 5ccf11819b0d1b9e6eb23f2b84f7dfb12474c17d Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Wed, 25 Jun 2025 10:52:04 +0200 Subject: [PATCH 18/25] update stellar mcp --- packages/core/stellar/package.json | 2 +- packages/mcp/src/stellar/schemas.ts | 13 +++++ packages/mcp/src/stellar/tools.ts | 2 + .../mcp/src/stellar/tools/fungible.test.ts | 3 +- packages/mcp/src/stellar/tools/fungible.ts | 3 +- .../src/stellar/tools/non-fungible.test.ts | 3 +- .../mcp/src/stellar/tools/stablecoin.test.ts | 58 +++++++++++++++++++ packages/mcp/src/stellar/tools/stablecoin.ts | 36 ++++++++++++ 8 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 packages/mcp/src/stellar/tools/stablecoin.test.ts create mode 100644 packages/mcp/src/stellar/tools/stablecoin.ts diff --git a/packages/core/stellar/package.json b/packages/core/stellar/package.json index bf893d84b..fa879606d 100644 --- a/packages/core/stellar/package.json +++ b/packages/core/stellar/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/wizard-stellar", - "version": "0.3.0", + "version": "0.2.2", "description": "A boilerplate generator to get started with OpenZeppelin Stellar Soroban Contracts", "license": "AGPL-3.0-only", "repository": "https://github.com/OpenZeppelin/contracts-wizard", diff --git a/packages/mcp/src/stellar/schemas.ts b/packages/mcp/src/stellar/schemas.ts index 483a6a687..51fca4bc0 100644 --- a/packages/mcp/src/stellar/schemas.ts +++ b/packages/mcp/src/stellar/schemas.ts @@ -5,6 +5,7 @@ import { stellarCommonDescriptions, stellarFungibleDescriptions, stellarNonFungibleDescriptions, + stellarStablecoinDescriptions, } from '@openzeppelin/wizard-common'; import type { KindedOptions } from '@openzeppelin/wizard-stellar'; @@ -16,11 +17,13 @@ function _typeAssertions() { [K in keyof KindedOptions]: Omit; } = { Fungible: z.object(fungibleSchema).parse({}), + Stablecoin: z.object(stablecoinSchema).parse({}), NonFungible: z.object(nonFungibleSchema).parse({}), }; } export const commonSchema = { + access: z.literal('ownable').or(z.literal('roles')).optional().describe(stellarCommonDescriptions.access), upgradeable: z.boolean().optional().describe(stellarCommonDescriptions.upgradeable), info: z .object({ @@ -40,6 +43,16 @@ export const fungibleSchema = { ...commonSchema, } as const satisfies z.ZodRawShape; +export const stablecoinSchema = { + ...fungibleSchema, + limitations: z + .literal(false) + .or(z.literal('allowlist')) + .or(z.literal('blocklist')) + .optional() + .describe(stellarStablecoinDescriptions.limitations), +} as const satisfies z.ZodRawShape; + export const nonFungibleSchema = { name: z.string().describe(commonDescriptions.name), symbol: z.string().describe(commonDescriptions.symbol), diff --git a/packages/mcp/src/stellar/tools.ts b/packages/mcp/src/stellar/tools.ts index 2d7543636..deface877 100644 --- a/packages/mcp/src/stellar/tools.ts +++ b/packages/mcp/src/stellar/tools.ts @@ -1,5 +1,6 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { registerStellarFungible } from './tools/fungible.js'; +import { registerStellarStablecoin } from './tools/stablecoin.js'; import { registerStellarNonFungible } from './tools/non-fungible.js'; import type { KindedOptions } from '@openzeppelin/wizard-stellar'; import type { RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js'; @@ -11,6 +12,7 @@ type StellarToolRegisterFunctions = { function getRegisterFunctions(server: McpServer): StellarToolRegisterFunctions { return { Fungible: () => registerStellarFungible(server), + Stablecoin: () => registerStellarStablecoin(server), NonFungible: () => registerStellarNonFungible(server), }; } diff --git a/packages/mcp/src/stellar/tools/fungible.test.ts b/packages/mcp/src/stellar/tools/fungible.test.ts index 71b0e687e..6b655f446 100644 --- a/packages/mcp/src/stellar/tools/fungible.test.ts +++ b/packages/mcp/src/stellar/tools/fungible.test.ts @@ -26,7 +26,7 @@ function assertHasAllSupportedFields( t: ExecutionContext, params: DeepRequired>, ) { - const _: DeepRequired> = params; + const _: DeepRequired = params; t.pass(); } @@ -47,6 +47,7 @@ test('all', async t => { premint: '1000000', mintable: true, upgradeable: true, + access: 'ownable', info: { license: 'MIT', }, diff --git a/packages/mcp/src/stellar/tools/fungible.ts b/packages/mcp/src/stellar/tools/fungible.ts index 01c8bcb34..c5fe304bd 100644 --- a/packages/mcp/src/stellar/tools/fungible.ts +++ b/packages/mcp/src/stellar/tools/fungible.ts @@ -10,7 +10,7 @@ export function registerStellarFungible(server: McpServer): RegisteredTool { 'stellar-fungible', makeDetailedPrompt(stellarPrompts.Fungible), fungibleSchema, - async ({ name, symbol, burnable, pausable, premint, mintable, upgradeable, info }) => { + async ({ name, symbol, burnable, pausable, premint, mintable, upgradeable, access, info }) => { const opts: FungibleOptions = { name, symbol, @@ -19,6 +19,7 @@ export function registerStellarFungible(server: McpServer): RegisteredTool { premint, mintable, upgradeable, + access, info, }; return { diff --git a/packages/mcp/src/stellar/tools/non-fungible.test.ts b/packages/mcp/src/stellar/tools/non-fungible.test.ts index 7f2782d63..ab20893e4 100644 --- a/packages/mcp/src/stellar/tools/non-fungible.test.ts +++ b/packages/mcp/src/stellar/tools/non-fungible.test.ts @@ -26,7 +26,7 @@ function assertHasAllSupportedFields( t: ExecutionContext, params: DeepRequired>, ) { - const _: DeepRequired> = params; + const _: DeepRequired = params; t.pass(); } @@ -49,6 +49,7 @@ test('all', async t => { upgradeable: true, mintable: true, sequential: true, + access: 'ownable', info: { license: 'MIT', }, diff --git a/packages/mcp/src/stellar/tools/stablecoin.test.ts b/packages/mcp/src/stellar/tools/stablecoin.test.ts new file mode 100644 index 000000000..74e00a0f4 --- /dev/null +++ b/packages/mcp/src/stellar/tools/stablecoin.test.ts @@ -0,0 +1,58 @@ +import type { TestFn, ExecutionContext } from 'ava'; +import _test from 'ava'; +import type { RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { registerStellarStablecoin } from './stablecoin'; +import type { DeepRequired } from '../../helpers.test'; +import { testMcpInfo, assertAPIEquivalence } from '../../helpers.test'; +import type { StablecoinOptions } from '@openzeppelin/wizard-stellar'; +import { stablecoin } from '@openzeppelin/wizard-stellar'; +import { stablecoinSchema } from '../schemas'; +import { z } from 'zod'; + +interface Context { + tool: RegisteredTool; + schema: z.ZodObject; +} + +const test = _test as TestFn; + +test.before(t => { + t.context.tool = registerStellarStablecoin(new McpServer(testMcpInfo)); + t.context.schema = z.object(stablecoinSchema); +}); + +function assertHasAllSupportedFields( + t: ExecutionContext, + params: DeepRequired>, +) { + const _: DeepRequired = params; + t.pass(); +} + +test('basic', async t => { + const params: z.infer = { + name: 'TestToken', + symbol: 'TST', + }; + await assertAPIEquivalence(t, params, stablecoin.print); +}); + +test('all', async t => { + const params: DeepRequired> = { + name: 'TestToken', + symbol: 'TST', + burnable: true, + pausable: true, + premint: '1000000', + mintable: true, + upgradeable: true, + access: 'ownable', + limitations: 'allowlist', + info: { + license: 'MIT', + }, + }; + assertHasAllSupportedFields(t, params); + await assertAPIEquivalence(t, params, stablecoin.print); +}); diff --git a/packages/mcp/src/stellar/tools/stablecoin.ts b/packages/mcp/src/stellar/tools/stablecoin.ts new file mode 100644 index 000000000..e132ee2d9 --- /dev/null +++ b/packages/mcp/src/stellar/tools/stablecoin.ts @@ -0,0 +1,36 @@ +import type { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { StablecoinOptions } from '@openzeppelin/wizard-stellar'; +import { stablecoin } from '@openzeppelin/wizard-stellar'; +import { safePrintRustCodeBlock, makeDetailedPrompt } from '../../utils'; +import { stablecoinSchema } from '../schemas'; +import { stellarPrompts } from '@openzeppelin/wizard-common'; + +export function registerStellarStablecoin(server: McpServer): RegisteredTool { + return server.tool( + 'stellar-stablecoin', + makeDetailedPrompt(stellarPrompts.Stablecoin), + stablecoinSchema, + async ({ name, symbol, burnable, pausable, premint, mintable, upgradeable, access, limitations, info }) => { + const opts: StablecoinOptions = { + name, + symbol, + burnable, + pausable, + premint, + mintable, + upgradeable, + access, + limitations, + info, + }; + return { + content: [ + { + type: 'text', + text: safePrintRustCodeBlock(() => stablecoin.print(opts)), + }, + ], + }; + }, + ); +} From ab239b9439ecb1f4113f272398fb515ef2d36103 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 25 Jun 2025 10:56:42 -0400 Subject: [PATCH 19/25] Add changesets for common and mcp --- .changeset/proud-brooms-happen.md | 7 +++++++ .changeset/soft-points-obey.md | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 .changeset/proud-brooms-happen.md create mode 100644 .changeset/soft-points-obey.md diff --git a/.changeset/proud-brooms-happen.md b/.changeset/proud-brooms-happen.md new file mode 100644 index 000000000..67de79cc9 --- /dev/null +++ b/.changeset/proud-brooms-happen.md @@ -0,0 +1,7 @@ +--- +'@openzeppelin/wizard-mcp': patch +--- + +Stellar: Add Stablecoin with Limitations and Access Control (ownable and roles). +- **Potentially breaking changes**: + - Use OpenZeppelin Stellar Soroban Contracts v0.3.0-rc.2 diff --git a/.changeset/soft-points-obey.md b/.changeset/soft-points-obey.md new file mode 100644 index 000000000..d3d9d23de --- /dev/null +++ b/.changeset/soft-points-obey.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard-common': patch +--- + +Stellar: Add Stablecoin with Limitations and Access Control (ownable and roles). From 8fe6163c7d2417b3342ac05332c6a497881261f3 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 26 Jun 2025 11:52:40 -0400 Subject: [PATCH 20/25] Add stellar stablecoin to MCP readme --- packages/mcp/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp/README.md b/packages/mcp/README.md index de4a81fc2..5da0c8afb 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -13,7 +13,7 @@ Provides tools to generate smart contract source code for the following language | --- | --- | | solidity | erc20, erc721, erc1155, stablecoin, rwa, account, governor, custom | | cairo | erc20, erc721, erc1155, account, multisig, governor, vesting, custom | -| stellar | fungible, non-fungible | +| stellar | fungible, stablecoin, non-fungible | | stylus | erc20, erc721, erc1155 | From 36a1de28fd4b5da13e015b88479e30acf673729a Mon Sep 17 00:00:00 2001 From: brozorec <9572072+brozorec@users.noreply.github.com> Date: Fri, 27 Jun 2025 13:27:55 +0200 Subject: [PATCH 21/25] merge master --- .changeset/busy-banks-shout.md | 5 + .changeset/metal-needles-dress.md | 5 + .github/workflows/test.yml | 2 +- README.md | 1 + packages/common/README.md | 2 + packages/core/stellar/src/contract.test.ts | 19 + packages/core/stellar/src/contract.test.ts.md | 48 + .../core/stellar/src/contract.test.ts.snap | Bin 589 -> 706 bytes packages/core/stellar/src/contract.ts | 13 + packages/core/stellar/src/print.ts | 10 + packages/core/stellar/src/set-info.ts | 9 +- .../core/stellar/src/zip-scaffold.test.ts.md | 10634 ++++++++++++++++ .../stellar/src/zip-scaffold.test.ts.snap | Bin 0 -> 8501 bytes packages/core/stellar/src/zip-scaffold.ts | 30 +- packages/mcp/README.md | 2 + packages/mcp/src/stellar/schemas.ts | 1 + .../mcp/src/stellar/tools/fungible.test.ts | 1 + .../src/stellar/tools/non-fungible.test.ts | 1 + .../mcp/src/stellar/tools/stablecoin.test.ts | 1 + .../function-definitions/stellar-shared.ts | 5 + packages/ui/src/stellar/InfoSection.svelte | 11 + 21 files changed, 10782 insertions(+), 18 deletions(-) create mode 100644 .changeset/busy-banks-shout.md create mode 100644 .changeset/metal-needles-dress.md create mode 100644 packages/core/stellar/src/zip-scaffold.test.ts.md create mode 100644 packages/core/stellar/src/zip-scaffold.test.ts.snap diff --git a/.changeset/busy-banks-shout.md b/.changeset/busy-banks-shout.md new file mode 100644 index 000000000..db2b25c75 --- /dev/null +++ b/.changeset/busy-banks-shout.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard-stellar': patch +--- + +Add security contact in contract info diff --git a/.changeset/metal-needles-dress.md b/.changeset/metal-needles-dress.md new file mode 100644 index 000000000..26b943b05 --- /dev/null +++ b/.changeset/metal-needles-dress.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard-mcp': patch +--- + +Add security contact for stellar diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 406e08677..4506bce76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -108,7 +108,7 @@ jobs: - name: Set up Stellar CLI if: matrix.package == 'stellar' - uses: stellar/stellar-cli@v22.5.0 + uses: stellar/stellar-cli@v22.8.1 - name: Install Scaffold & Registry CLIs if: matrix.package == 'stellar' diff --git a/README.md b/README.md index c96550767..947478b8b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Cairo NPM Package](https://img.shields.io/npm/v/@openzeppelin/wizard-cairo?color=%23e55233&label=%40openzeppelin%2Fwizard-cairo)](https://www.npmjs.com/package/@openzeppelin/wizard-cairo) [![Stellar NPM Package](https://img.shields.io/npm/v/@openzeppelin/wizard-stellar?color=%23e55233&label=%40openzeppelin%2Fwizard-stellar)](https://www.npmjs.com/package/@openzeppelin/wizard-stellar) [![Stylus NPM Package](https://img.shields.io/npm/v/@openzeppelin/wizard-stylus?color=%23e55233&label=%40openzeppelin%2Fwizard-stylus)](https://www.npmjs.com/package/@openzeppelin/wizard-stylus) +[![MCP NPM Package](https://img.shields.io/npm/v/@openzeppelin/wizard-mcp?label=%40openzeppelin%2Fwizard-mcp)](https://www.npmjs.com/package/@openzeppelin/wizard-mcp) [![Netlify Status](https://api.netlify.com/api/v1/badges/ca9b53e1-44eb-410d-aac7-31b2f5399b68/deploy-status)](https://app.netlify.com/sites/openzeppelin-contracts-wizard/deploys) Contracts Wizard is a web application to interactively build a contract out of components from OpenZeppelin Contracts. Select the kind of contract that you want, set your parameters and desired features, and the Wizard will generate all of the code necessary. The resulting code is ready to be compiled and deployed, or it can serve as a starting point and customized further with application specific logic. diff --git a/packages/common/README.md b/packages/common/README.md index c231dd9b2..b8fc10add 100644 --- a/packages/common/README.md +++ b/packages/common/README.md @@ -1,3 +1,5 @@ # @openzeppelin/wizard-common +[![NPM Package](https://img.shields.io/npm/v/@openzeppelin/wizard-common)](https://www.npmjs.com/package/@openzeppelin/wizard-common) + Common library for OpenZeppelin Contracts Wizard components. Used internally. diff --git a/packages/core/stellar/src/contract.test.ts b/packages/core/stellar/src/contract.test.ts index 87227520b..c66010496 100644 --- a/packages/core/stellar/src/contract.test.ts +++ b/packages/core/stellar/src/contract.test.ts @@ -82,3 +82,22 @@ test('contract with sorted use clauses', t => { Foo.addUseClause('another::library', 'self', { alias: 'custom1' }); t.snapshot(printContract(Foo)); }); + +test('contract with documentation', t => { + const Foo = new ContractBuilder('Foo'); + Foo.addDocumentation('Some documentation'); + t.snapshot(printContract(Foo)); +}); + +test('contract with security info', t => { + const Foo = new ContractBuilder('Foo'); + Foo.addSecurityTag('security@example.com'); + t.snapshot(printContract(Foo)); +}); + +test('contract with security info and documentation', t => { + const Foo = new ContractBuilder('Foo'); + Foo.addSecurityTag('security@example.com'); + Foo.addDocumentation('Some documentation'); + t.snapshot(printContract(Foo)); +}); diff --git a/packages/core/stellar/src/contract.test.ts.md b/packages/core/stellar/src/contract.test.ts.md index 5de36f693..9b998e470 100644 --- a/packages/core/stellar/src/contract.test.ts.md +++ b/packages/core/stellar/src/contract.test.ts.md @@ -140,3 +140,51 @@ Generated by [AVA](https://avajs.dev). #[contract]␊ pub struct Foo;␊ ` + +## contract with documentation + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ + ␊ + //! Some documentation␊ + #![no_std]␊ + ␊ + #[contract]␊ + pub struct Foo;␊ + ` + +## contract with security info + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ + ␊ + //! # Security␊ + //!␊ + //! For security issues, please contact: security@example.com␊ + #![no_std]␊ + ␊ + #[contract]␊ + pub struct Foo;␊ + ` + +## contract with security info and documentation + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0-rc.2␊ + ␊ + //! Some documentation␊ + ␊ + //! # Security␊ + //!␊ + //! For security issues, please contact: security@example.com␊ + #![no_std]␊ + ␊ + #[contract]␊ + pub struct Foo;␊ + ` diff --git a/packages/core/stellar/src/contract.test.ts.snap b/packages/core/stellar/src/contract.test.ts.snap index aafb641592048ac0c340f457be75e4d249a2a5f9..da7b095de83342549efe65c7a2e81b1efa99ec97 100644 GIT binary patch literal 706 zcmV;z0zLgfRzVSUp_(DJZJtLmIzesn zkrQL`fgg(q00000000B+mOpRPKor0e5GwMwh6-epOQibSz zer`{k?^$=$dHAH1aBvAXXzPjNx8n8rh!mJ1+!Yg5S;Yot(872uzp`c|^N~WPjngXU^ zsVKm}^M(5T`a(_fdef`SZY$~-8@4^KvVB}++e^{_jMXW(utK!x6&QOrdmQ;2IjL?I zY2Xy%FxGDe>-&^p^<5NkBHLbe&Mv`$2LO=eM+!QfKF~Sz;m)#pIbY;thoKl(QpK$x zH8;PQ#}B>3p0?V_+PYDLvEU?0U057#@wRI*DpI=&1B^%yH{e$Jf#dFOdFtGe-9>HL zPJ6V+D~uAczA!in1_F# zhgT7((_6E?H`D)M~rxDEKuY1x)3Os^5Y^ z838lEQ)4NdJj6W~ oClc$N;xk*SonQ%=3Qy^gt@4Z`?&pM?WO73P0Je)5X6+0B04SSQ2mk;8 literal 589 zcmV-T0L?Jq# zpPLisd)D1E6jd46nD_<^`!M8df&jUhRI*(7Uagl>D?*EctlAi&ms$wLR`f9 zUBddgW>_#i&Tl6$TiQU0i@Kt4~Yr9#z-Q9q|w?i `//! ${documentation}`); +} + +function printSecurityTag(securityContact: string) { + return ['//! # Security', '//!', `//! For security issues, please contact: ${securityContact}`]; +} diff --git a/packages/core/stellar/src/set-info.ts b/packages/core/stellar/src/set-info.ts index 232662ae8..c22f9e6ac 100644 --- a/packages/core/stellar/src/set-info.ts +++ b/packages/core/stellar/src/set-info.ts @@ -1,15 +1,22 @@ import type { ContractBuilder } from './contract'; +export const TAG_SECURITY_CONTACT = `@custom:security-contact`; + export const infoOptions = [{}, { license: 'WTFPL' }] as const; export const defaults: Info = { license: 'MIT' }; export type Info = { license?: string; + securityContact?: string; }; export function setInfo(c: ContractBuilder, info: Info): void { - const { license } = info; + const { securityContact, license } = info; + + if (securityContact) { + c.addSecurityTag(securityContact); + } if (license) { c.license = license; diff --git a/packages/core/stellar/src/zip-scaffold.test.ts.md b/packages/core/stellar/src/zip-scaffold.test.ts.md new file mode 100644 index 000000000..fe99d1e3f --- /dev/null +++ b/packages/core/stellar/src/zip-scaffold.test.ts.md @@ -0,0 +1,10634 @@ +# Snapshot report for `src/zip-scaffold.test.ts` + +The actual snapshot is saved in `zip-scaffold.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## fungible simple + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ + use stellar_fungible::{self as fungible, FungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, ());␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible full + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + #[when_not_paused]␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible burnable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol};␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible mintable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ + use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible burnable mintable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible burnable pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible mintable pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + #[when_not_paused]␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable simple + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{self as fungible, FungibleToken};␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable full + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + #[when_not_paused]␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable burnable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable mintable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable burnable mintable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable burnable pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{␊ + self as fungible, burnable::FungibleBurnable, FungibleToken, mintable::FungibleMintable␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + fungible::burnable::burn(e, &from, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + fungible::burnable::burn_from(e, &spender, &from, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## fungible upgradable mintable pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_fungible::{self as fungible, FungibleToken, mintable::FungibleMintable};␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyToken;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyTokenError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env, recipient: Address, owner: Address) {␊ + fungible::metadata::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ + fungible::mintable::mint(e, &recipient, 2000000000000000000000);␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + fn total_supply(e: &Env) -> i128 {␊ + fungible::total_supply(e)␊ + }␊ + ␊ + fn balance(e: &Env, account: Address) -> i128 {␊ + fungible::balance(e, &account)␊ + }␊ + ␊ + fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ + fungible::allowance(e, &owner, &spender)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + fungible::transfer(e, &from, &to, amount);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ + fungible::transfer_from(e, &spender, &from, &to, amount);␊ + }␊ + ␊ + fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ + fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + }␊ + ␊ + fn decimals(e: &Env) -> u32 {␊ + fungible::metadata::decimals(e)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + fungible::metadata::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + fungible::metadata::symbol(e)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl FungibleMintable for MyToken {␊ + #[when_not_paused]␊ + fn mint(e: &Env, account: Address, amount: i128) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + fungible::mintable::mint(e, &account, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyToken {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyTokenError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyToken, MyTokenClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyToken, (Address::generate(&env),Address::generate(&env)));␊ + let client = MyTokenClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyToken"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--recipient \\"ADD_RECIPIENT_ADDRESS_HERE\\" --owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible simple + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_non_fungible::{Base, NonFungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Base;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, ());␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible full except sequential mintable enumerable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_non_fungible::{␊ + Base, burnable::NonFungibleBurnable, consecutive::{NonFungibleConsecutive, Consecutive},␊ + ContractOverrides, NonFungibleToken␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyNFTError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + ␊ + #[when_not_paused]␊ + pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + Consecutive::batch_mint(e, &to, amount)␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Consecutive;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl NonFungibleBurnable for MyNFT {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, token_id: u32) {␊ + Self::ContractType::burn(e, &from, token_id);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ + Self::ContractType::burn_from(e, &spender, &from, token_id);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleConsecutive for MyNFT {}␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyNFT {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyNFT {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible burnable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_non_fungible::{Base, burnable::NonFungibleBurnable, NonFungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Base;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl NonFungibleBurnable for MyNFT {␊ + fn burn(e: &Env, from: Address, token_id: u32) {␊ + Self::ContractType::burn(e, &from, token_id);␊ + }␊ + ␊ + fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ + Self::ContractType::burn_from(e, &spender, &from, token_id);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, ());␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible consecutive + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String, Symbol, symbol_short};␊ + use stellar_non_fungible::{␊ + Base, consecutive::{NonFungibleConsecutive, Consecutive}, ContractOverrides,␊ + NonFungibleToken␊ + };␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + ␊ + pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + Consecutive::batch_mint(e, &to, amount)␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Consecutive;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl NonFungibleConsecutive for MyNFT {}␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_non_fungible::{Base, NonFungibleToken};␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyNFTError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Base;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyNFT {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible upgradeable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_non_fungible::{Base, NonFungibleToken};␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyNFTError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Base;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyNFT {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible sequential + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_non_fungible::{Base, NonFungibleToken};␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Base;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, ());␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible burnable pausable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_non_fungible::{Base, burnable::NonFungibleBurnable, NonFungibleToken};␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyNFTError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Base;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl NonFungibleBurnable for MyNFT {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, token_id: u32) {␊ + Self::ContractType::burn(e, &from, token_id);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ + Self::ContractType::burn_from(e, &spender, &from, token_id);␊ + }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyNFT {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible enumerable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_non_fungible::{␊ + Base, ContractOverrides, enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ + };␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Enumerable;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl NonFungibleEnumerable for MyNFT {}␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, ());␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible burnable enumerable + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_non_fungible::{␊ + Base, burnable::NonFungibleBurnable, ContractOverrides,␊ + enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ + };␊ + ␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Enumerable;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl NonFungibleBurnable for MyNFT {␊ + fn burn(e: &Env, from: Address, token_id: u32) {␊ + Self::ContractType::burn(e, &from, token_id);␊ + }␊ + ␊ + fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ + Self::ContractType::burn_from(e, &spender, &from, token_id);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl NonFungibleEnumerable for MyNFT {}␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, ());␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] + +## nonfungible full except consecutive + +> Snapshot 1 + + [ + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + ␊ + ␊ + use soroban_sdk::{␊ + Address, contract, contracterror, contractimpl, Env, panic_with_error, String, Symbol,␊ + symbol_short␊ + };␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_non_fungible::{␊ + Base, burnable::NonFungibleBurnable, ContractOverrides,␊ + enumerable::{NonFungibleEnumerable, Enumerable}, NonFungibleToken␊ + };␊ + use stellar_pausable::{self as pausable, Pausable};␊ + use stellar_pausable_macros::when_not_paused;␊ + use stellar_upgradeable::UpgradeableInternal;␊ + use stellar_upgradeable_macros::Upgradeable;␊ + ␊ + const OWNER: Symbol = symbol_short!("OWNER");␊ + ␊ + #[derive(Upgradeable)]␊ + #[contract]␊ + pub struct MyNFT;␊ + ␊ + #[contracterror]␊ + #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]␊ + #[repr(u32)]␊ + pub enum MyNFTError {␊ + Unauthorized = 1,␊ + }␊ + ␊ + #[contractimpl]␊ + impl MyNFT {␊ + pub fn __constructor(e: &Env, owner: Address) {␊ + Base::set_metadata(e, String::from_str(e, "www.mytoken.com"), String::from_str(e, "MyNFT"), String::from_str(e, "MNFT"));␊ + e.storage().instance().set(&OWNER, &owner);␊ + }␊ + ␊ + #[when_not_paused]␊ + pub fn sequential_mint(e: &Env, to: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + owner.require_auth();␊ + Enumerable::sequential_mint(e, &to);␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl NonFungibleToken for MyNFT {␊ + type ContractType = Enumerable;␊ + ␊ + fn owner_of(e: &Env, token_id: u32) -> Address {␊ + Self::ContractType::owner_of(e, token_id)␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer(e, &from, &to, token_id);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {␊ + Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);␊ + }␊ + ␊ + fn balance(e: &Env, owner: Address) -> u32 {␊ + Self::ContractType::balance(e, &owner)␊ + }␊ + ␊ + fn approve(␊ + e: &Env,␊ + approver: Address,␊ + approved: Address,␊ + token_id: u32,␊ + live_until_ledger: u32,␊ + ) {␊ + Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);␊ + }␊ + ␊ + fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {␊ + Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);␊ + }␊ + ␊ + fn get_approved(e: &Env, token_id: u32) -> Option
{␊ + Self::ContractType::get_approved(e, token_id)␊ + }␊ + ␊ + fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {␊ + Self::ContractType::is_approved_for_all(e, &owner, &operator)␊ + }␊ + ␊ + fn name(e: &Env) -> String {␊ + Self::ContractType::name(e)␊ + }␊ + ␊ + fn symbol(e: &Env) -> String {␊ + Self::ContractType::symbol(e)␊ + }␊ + ␊ + fn token_uri(e: &Env, token_id: u32) -> String {␊ + Self::ContractType::token_uri(e, token_id)␊ + }␊ + }␊ + ␊ + //␊ + // Extensions␊ + //␊ + ␊ + #[contractimpl]␊ + impl NonFungibleBurnable for MyNFT {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, token_id: u32) {␊ + Self::ContractType::burn(e, &from, token_id);␊ + }␊ + ␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {␊ + Self::ContractType::burn_from(e, &spender, &from, token_id);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl NonFungibleEnumerable for MyNFT {}␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + impl UpgradeableInternal for MyNFT {␊ + fn _require_auth(e: &Env, operator: &Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != *operator {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + operator.require_auth();␊ + }␊ + }␊ + ␊ + #[contractimpl]␊ + impl Pausable for MyNFT {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ + }␊ + ␊ + fn pause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::pause(e, &caller);␊ + }␊ + ␊ + fn unpause(e: &Env, caller: Address) {␊ + let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");␊ + if owner != caller {␊ + panic_with_error!(e, MyNFTError::Unauthorized);␊ + }␊ + pausable::unpause(e, &caller);␊ + }␊ + }␊ + `, + `#![cfg(test)]␊ + ␊ + extern crate std;␊ + ␊ + use soroban_sdk::{ testutils::Address as _, Address, Env, String };␊ + ␊ + use crate::contract::{ MyNFT, MyNFTClient };␊ + ␊ + #[test]␊ + fn initial_state() {␊ + let env = Env::default();␊ + ␊ + let contract_addr = env.register(MyNFT, (Address::generate(&env),));␊ + let client = MyNFTClient::new(&env, &contract_addr);␊ + ␊ + assert_eq!(client.name(), String::from_str(&env, "MyNFT"));␊ + }␊ + ␊ + // Add more tests bellow␊ + `, + `#![no_std]␊ + #![allow(dead_code)]␊ + ␊ + mod contract;␊ + mod test;␊ + `, + `[package]␊ + name = "non-fungible-contract"␊ + edition.workspace = true␊ + license.workspace = true␊ + publish = false␊ + version.workspace = true␊ + ␊ + [lib]␊ + crate-type = ["cdylib"]␊ + doctest = false␊ + ␊ + [dependencies]␊ + stellar-default-impl-macro = { workspace = true }␊ + stellar-fungible = { workspace = true }␊ + stellar-non-fungible = { workspace = true }␊ + stellar-pausable = { workspace = true }␊ + stellar-pausable-macros = { workspace = true }␊ + stellar-upgradeable = { workspace = true }␊ + stellar-upgradeable-macros = { workspace = true }␊ + soroban-sdk = { workspace = true }␊ + ␊ + [dev-dependencies]␊ + soroban-sdk = { workspace = true, features = ["testutils"] }␊ + ␊ + `, + `#!/usr/bin/env bash␊ + #␊ + # setup.sh␊ + # ␊ + # This script is meant to set up a Scaffold project and insert the Wizard's contracts in the project␊ + ␊ + check_is_installed() {␊ + if ! which "$1" &> /dev/null; then␊ + echo "❌ $1 command not found."␊ + echo "Install $2 and try again, you can find installation guides in the README."␊ + exit 1␊ + fi␊ + }␊ + ␊ + scaffold() {␊ + tmp_folder="tmp"␊ + stellar scaffold init "$tmp_folder"␊ + ␊ + rm -rf "$tmp_folder/contracts"␊ + ␊ + local current_directory␊ + current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"␊ + ␊ + shopt -s dotglob␊ + ␊ + cp -a "$current_directory/$tmp_folder"/. "$current_directory"/␊ + rm -rf "$current_directory/$tmp_folder"␊ + }␊ + ␊ + init_git(){␊ + git init␊ + git add .␊ + git commit -m "openzeppelin: add wizard output" --quiet␊ + }␊ + ␊ + ␊ + # Update environments.toml: remove original contracts and insert wizard's contract␊ + setup_environment() {␊ + local file="environments.toml"␊ + local temp␊ + temp="$(mktemp)"␊ + ␊ + local in_dev_contracts=0␊ + local skip_entry=0␊ + local contract_entry_inserted=0␊ + insert_contract_entry() {␊ + {␊ + printf '%s\\n' "[development.contracts.non_fungible_contract]" \\␊ + "client = true" "" \\␊ + "# If your contract has a \\\`__constructor\\\`, specify your arguments to it here." \\␊ + "# These are the same arguments you could pass after the \\\`--\\\` in a call to" \\␊ + "# \\\`stellar contract deploy\\\`" \\␊ + "# Only available in \\\`development\\\` and \\\`test\\\` environments" \\␊ + "# TODO add appropriate values for for the constructors arguments" \\␊ + "constructor_args = \\"\\"\\"" \\␊ + "--owner \\"ADD_OWNER_ADDRESS_HERE\\"" \\␊ + "\\"\\"\\"" \\␊ + ""␊ + } >> "$temp"␊ + }␊ + ␊ + while IFS= read -r line; do␊ + if [[ $contract_entry_inserted -eq 0 && $line == '[staging.network]' ]]; then␊ + insert_contract_entry␊ + contract_entry_inserted=1␊ + fi␊ + ␊ + if [[ $line =~ ^\\[development\\.contracts\\]$ ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + in_dev_contracts=1␊ + skip_entry=0␊ + continue␊ + fi␊ + ␊ + if [[ $line =~ ^\\[[^]]+\\]$ ]]; then␊ + if (( in_dev_contracts )) && [[ $line =~ ^\\[development\\.contracts\\..+\\]$ ]]; then␊ + skip_entry=1␊ + in_dev_contracts=0␊ + continue␊ + fi␊ + in_dev_contracts=0␊ + skip_entry=0␊ + printf '%s\\n' "$line" >> "$temp"␊ + continue␊ + fi␊ + ␊ + if (( skip_entry )); then␊ + continue␊ + fi␊ + ␊ + if (( in_dev_contracts )); then␊ + if [[ $line =~ ^[[:space:]]*# ]]; then␊ + printf '%s\\n' "$line" >> "$temp"␊ + fi␊ + continue␊ + fi␊ + ␊ + printf '%s\\n' "$line" >> "$temp"␊ + done < "$file"␊ + ␊ + mv "$temp" "$file"␊ + }␊ + ␊ + ␊ + update_cargo() {␊ + cp Cargo.toml Cargo.toml.bak␊ + ␊ + cat < deps.tmp␊ + stellar-default-impl-macro = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-non-fungible = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-pausable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + stellar-upgradeable-macros = { git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "v0.2.0" }␊ + soroban-sdk = { version = "22.0.8" }␊ + ␊ + EOF␊ + ␊ + awk '␊ + BEGIN {␊ + inserted = 0␊ + deps = ""␊ + while ((getline line < "deps.tmp") > 0) {␊ + deps = deps line "\\n"␊ + }␊ + close("deps.tmp")␊ + }␊ + /^\\[workspace.dependencies\\]/ {␊ + in_deps = 1␊ + print␊ + if (!inserted) {␊ + printf "%s", deps␊ + inserted = 1␊ + }␊ + next␊ + }␊ + /^\\[/ { in_deps = 0 }␊ + in_deps { next }␊ + { print }␊ + ' Cargo.toml.bak > Cargo.toml␊ + ␊ + rm deps.tmp␊ + rm Cargo.toml.bak␊ + }␊ + ␊ + build_contracts() {␊ + cargo build␊ + }␊ + ␊ + install_npm_dependencies() {␊ + if ! npm install --silent; then␊ + echo "❌ Failed to set up the project."␊ + exit 1␊ + fi␊ + }␊ + ␊ + ␊ + ################␊ + ##### Start ####␊ + ################␊ + ␊ + echo "⚙️ Checking dependencies requirement"␊ + check_is_installed git "Git"␊ + check_is_installed cargo "Rust"␊ + check_is_installed stellar "Scaffold"␊ + check_is_installed docker "Docker"␊ + check_is_installed node "Node"␊ + ␊ + ␊ + if ! [ -f "environments.toml" ]␊ + then␊ + echo "🏗️ Building Scaffold project"␊ + ␊ + scaffold␊ + ␊ + setup_environment␊ + ␊ + update_cargo␊ + ␊ + build_contracts␊ + ␊ + install_npm_dependencies␊ + ␊ + init_git␊ + ␊ + echo "✅ Installation complete" ␊ + else␊ + echo "✅ Scaffold project already initialized."␊ + fi␊ + `, + `# Sample Scaffold Project␊ + ␊ + This project demonstrates a basic Scaffold use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, and a script that initiate a Stellar Scaffold project with this contract. [Scaffold Stellar](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#scaffold-stellar) is a convention-over-configuration toolkit for blockchain and distributed application development on the Stellar network. It provides a seamless development experience through CLI tools, smart contract management, and deployment utilities.␊ + ␊ + ## Installing dependencies␊ + ␊ + - See [Git installation guide](https://github.com/git-guides/install-git).␊ + - See [Rust installation guide](https://www.rust-lang.org/tools/install).␊ + - See [Scaffold CLI installation guide](https://github.com/AhaLabs/scaffold-stellar?tab=readme-ov-file#quick-start).␊ + - See [Docker installation guide](https://docs.docker.com/engine/install/).␊ + - See [Node installation guide](https://nodejs.org/en/download).␊ + ␊ + ## Initializing the project␊ + ␊ + \`\`\`␊ + bash setup.sh␊ + \`\`\`␊ + ␊ + ## Resolve any TODOs ␊ + ␊ + Search for any TODO comments in the project and resolve them (search for TODO with your code editor).␊ + ␊ + ␊ + ## Testing the contract␊ + ␊ + \`\`\`␊ + cargo test␊ + \`\`\`␊ + ␊ + ## Deploying the contract␊ + ␊ + \`\`\`␊ + stellar scaffold watch --build-clients␊ + \`\`\`␊ + ␊ + ## Deploying the contract and run the Scaffold UI app␊ + ␊ + \`\`\`␊ + npm run dev␊ + \`\`\`␊ + `, + ] diff --git a/packages/core/stellar/src/zip-scaffold.test.ts.snap b/packages/core/stellar/src/zip-scaffold.test.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..9135ba720ba06c9704e04bbc977f3b1912bd044c GIT binary patch literal 8501 zcmbW+WmJ?~zW{JhP#Pqa9;LfOx}*n`?rxY-VCYafB$RHYVQ3_V6bA%`7zP+R6hXQ> zl#8D8zW2T7o^|h9=lQK?@3o&VdwI2ga5+uQSjuC(iBclUaoc6lyKi-}OIKVM?Y&l~=gS-%Luw=V*;?`-RRXT1u&}z8ClWwR~T2XG)?VnHEq>^2K~3Ixaf; zOZ8ZZRuXQwhNLPBOMgbR&GJ@vR$n*SSZ~LnVy#M=IqdxBmAYR_2GIbo^Wd_drh`{> zN1KlpJLP&C+O^Dd0&KQQ=vUG+XkY~GA($^z&wdc$JjOiG$WgAk%AGXpoFZ}5pdQW{ zt{#)$OjHox)UxI1QZ)K)Gt*wMw;xrW_xM6xNH(4)eVf*7sSz~c;ptRhRQq}eYW~FB zcjaBbT2U{NptCN-v4%|%YsKamde)q6TRAomlhHc+V@ISWb|OB1z?iO0&n1&uko zwiFJm&{iv(sQxT~n`dfggc$2syI zOqK?nBP7U9i#=j1EW#N(W1LIvQ?QLzN{uUxtNPW)Wu`&}iA9kpqu_xZQeAk>gv3D2 zgyxIfh?1l+>tae1f#yNO2=T|9hX|CBOv276tTGcAH~sYMQgb)ryZ%<>%+ZNia!vq9 z@qA^iqK`w_0Y>5afd1>w(!fI}-N+rfg-YY|9>t;BO11LJRJVpmG3gvV#8U-ueVvQ@ zJR6M$G&Xr>wwlR6n5*J+3VWKxc1p(~PVczbS&VM-<>d*diRlQ+ex5a^OvFgpegV>^ z6tti}?<-!nN+aE5+3Gr_bw+pHtvGTR?DH@X1T%&@hDO;)m+oD9?3^Lb64Gn<8xjQ+Aoida&EluT8K&T7k=PBX%YMQrfSx8w}B z$EHhDbFF=x<+WBtl{Wo>?|}SQc!{{H3#>x8Z6o%6gEtqqQ+u^92BTE zsHcP7=_!kcyPDe*BkkI_@D8+@kCEmgPpNX-IUcWBKh56$;57X@M#|eUJUX6LlBxGf zL1170Vy@09g|4cSW8Gt*&L82Zfkx=5+brD|=H{L|g&w5!?K`KVvqB17=uJRnXyJxo zeTt_RkCk=0qTdp`tFUW~!9W?|kO6ZKV`sg>Z;b=wa2mp+Dn}TFf;Y8i{DdytN-b*| zXibEudvBmm;R{+mf1+Mt2-IsJRH!c0rx+@aDUUp78=y|2sy0Gr)R&(%owo%a9WFJd zg3I-0^F5CEw|U}05hh_aW!$3~sj%%2+-S%+VANL_(x|r6xY1Z%yS+@S-lF$?0Xxh4 zLqm|NqD|AG8DU3{y|}Rrhqv4JzT50XxdRZmYGn*TDB0dn7w%c^`VlODbE+o>MA?Hj zeVP;TR)2KdYR-+Ko4CAiy**+_yTo~u8wiOM$#8Z@aKDLZ;VvI7DmR82MLaQSJjT>t z(G?ah&sxeaZ0lOPJet%qY5fv58Lj})soEFSPIeB}n?$teOlU?zBf5k-t5GgTv}WKn z^c~G1Qy^;EBhmExG#gb6b<8>`A)9@EbA1Np@DM>%5yctyfy zp3L!!ltIV7^?H;t8B%FWL#b2UGG>m6&%A1dgvppSqd`NU(KfS9Lvw;ztzy24a_5n9 zm)XH(JI$ty`#T`E58l(J&MZ1;M`xCT^~NpNXMzb}Qwu*W-}T`it%vvnY5Jl%)8sih zMU@b4&U$ZZ{n)WK1>x99Lg>R(Jrkko(Qyqe%pRZP*ziK8GV6fqJFASw&xF)rRfih- zLbhhBb?>ia^fU{wufZ>xA;3e;%sS)2fcfAp_I}09gSmp#0TUiARKU;oP(hvpgVCcP zbu#Z~_KlEPfvmx0;42f&0!6op4>PZg8_g#*QHZY@G!jjdtw{EfrY3f!8ZPRE zQZI5Wx3SCQndmNZh-@e#M8D@S^BvizZ90%80z&=V;ObQ~5QE12wb!0NnnC;Us)}9V zba*Mp&L-g#x)$YiZ?g( zYBru6^6RoSowL>F9YLcF!LzH&`;*5K>pcDn_GZzO2+qUMqlT2TvIf!|6Hch$e7JW` zl0bs41KbC))ndqV$=Sk_@@}|gy^g!Q`D$ikW0uspaMF=wOowsEUa1uX7R3)Qd@aG@ zFK(JXMa;AnJ(tEUqUspuCi1-V$$+hO#;9wfO~M`59h4f!G_?u&FLU4jWW!tcd2-(lbASJu4Y3}?@!-XSN3%Iqcd4&c^_W`R zi=LP50pF+?HG8O=;%!_|w=-7v)8Ox#x6#FGf1;VDCA{E$Q52;SqCVHEU@rjqY_|%y z-c`!Pt18t^H7`h8+O4^wU6v7mIQI~^&k|hV!`%&8Qlm*LxjP!`Xr*&0mJ(iYLVFC+ z13_|W-=x4>#qK;)tv)MP((Dj(q4GXPFAJR6C3gci&66l^y&4sU-ty=D7Gzw$T%!9_ z(kFN+ojjh$I!DH?uddffPxKB93_2Uj*eDD{Ka`db`zgGMM?ewmYFNEzY zE7YS2=+TbSyK8p@eV07Ga_fnvQ~HDAq=H#w5bWz!*KOFI(NK{RREQ5=5AWVs=l&0w zbMhQ2t%fCG)3%dwdt$SBduxnDHJ`l{1H+AZzXP@E=x5$$tiFMKT$j<$#>(f>X5+7vR zPx}_l{`UdN#>MxZ8E@yX9I`3v+}+SSbOXA8@tyQ$N{liXp0X-W1T5m zV@<)Oyk!(6_p|AJuU`jI8E|^!|H$G3Hym2)rtAQdD0WM1XFv2%JsTJ_abww)6K>y` zSpcl2+EuJCbRZJi&=DNDM;dX?EpWH?rI-m_6K;iSzpU%$p2QQHKA zE`fpVmaRlcnsOsvL$7wWQ=UF<1iK`aBEDX9%B*Y$pzJC!N=3ZTT(8`YMX@ishY6^ z!Wsda$-KAMb57^Yn@tX~UJgUHu3;K#RY1GQ+st*o1}R5%pIzIB1P=9G9x1O94%mwO zCF#xi#q&s#Lz;T*Xf6-qD_7R&Oa@{q-6rY;mt2|+7zO9;wdX?Ex)|V#qIzkfTqXpJ z-6~R(RMD;$P0OW2<$mfxd~w5#!wHv{4=r2p@R5N1v@lGoc8?=OQQ@`qgW3={w@GEa zMkC{;IY6NfJ92-pLUIpN2%~e_CMd~#r^Vc$S8m1CjMI|NGAGB9uE>xa9v-foow1aY z@`#dP$2o+?MIJnMElF{@+F>v}v1;Mze=2~d)%vxHvUb1508x{&86542hCq3w?xzr- zhZnX>Es%4;;4qVoHjZ$JT8AbYKC9cvn2QaSOnN&T1KwT|v<;7b8qDDZPlDqAq$#Mo zFJjy1Z*hDIC_8WSIPItb{Lq@U%uWsPILfU4rdDp4dM{Z00nx3{5Ay~Eru4!sXQML? z9|V^4CtS>Tj^?s0VhyJkIy~dMkno{wJeGCdoWgX2NO5NdqfZ(SUn;61%FU=wV^fmPER@Rw zqyuQw44zGb7q}M9(k%k)UVJ@iKRC`3-R0rpPFbvf?ro)HYOv5er_OTs*i7}YJCKba zpFCfPC6ZAM@E5(R$v*`Y*?(KFiXQdMT(FC``|v<-i154${ywIzTeo#4an-%XuwyrA z7yQ`bq5rLMWT>cNl6|(L3#_Qk`G!$af+UjPC}R-xJ{=Un@((-1xD zawIDbtcv6yEh+5k&;y=1Q7sL@2?_0gKZ@hD(Wem0{B;A@DeJaq}q z`Hqvk{WJL6vS6L~gXAKxhJls*GYu6y4KG{%2P{n3QW7CY1WAu0svG&rzNHHhvOvNv zLjQqTt>e=*R)2d4Es20z4k=FF0?+{`a+^hShua+Co;$jlQ>j=NK5w#g<1Sh#BpnEGNkj&s)RUMr0y2X(_G5vHFi^&D;syvl^)8Dbt zAf8#SDp*ppFAqp|@ZACDe9B0^LqLLSc}6lY8IYEC!iY2@a1^m0A;7$RH$_rIjCpCf z@L9-`!Csx-5N2hs{=~Aoau=$A^(1U^PPjUnF$qUNoJBMASB(oI00oG__(L>8XhQ$= zsE^53;6H_HMEr#`o4|R=TU&PwB|pePcs0}dvN|iSxPMII0lR#MZ)RdsaRj>G!r)!f z=@V8s(Ju^660dR6M?Q{xr2ElpUbo19)Q(Z^Po+)1NBkGa>%V^a`!=|6kC#yfcW*aI zb#FJAT)snGwQm-9@8Gfo_k0*;^>uNV-s!BV=V~<6l)>;93*^G9<6r3LCPW#}nDRAl z?PLB!bhh181atoPEKa_uM(uX8;GTab2h`v6{7DF~s74%nCM|*6-9!^bEEpMz8*;C_t%1F)f8ugO6iJ*J=qoGqc`%qsZunG-^k9h` z@GFHn69OD6C;;6H0VQdaV|=%*Z{2uk{>YKpk-6Av0q&H3Z=LiF&rR{{pEyua@Bazl zT@=Y;Vh)DHBu?htY%@3S!~=P0_aH-dUpKsG?%+bE5bICBJ!E4Ev6ctL7tL)2=k74C z(VE7-aL?U~jZ*Pb`2>3S&}p$)PVt$T$v-f=@6m|qPxHl5bpT52LfRv{^%6 z-k<7G)*N_kGC@-gcRy;h^D1o}A_|$d69O-`csKCIv;}T1h^W{G>~T7e;M0=r-&|v4 z(wL5Gn-2VVQ&9Tky1{)V6{Db4Ga%~T_PeW>_i3yA7bX>J1q~Eb7%lFlRvI(m+RhKO ze7C8-YS1QBB&_RxZCpl+dkj_VQFEQ@@CMgqksd{b$&6?9Qo4g z_B=gjnr>_}*6{}y1AfdEd=Fd%KGhk!b=-Knnz|txrH@*&wOgxPELZxr!B?t#R6^OX z;s{*mTMu;_z~n&+qqU3b~z0#X|v0XAC+;c7v1q7&bd@PHELJA}L!4IPb2 z+mAiAgP(2!(_QbnrD4I}Iwu*b$@>!$%Oy zf10BaF75f_!h2Z?J^wjS#!EDz>1^-)!-Ct8vs+4c3U;ON$)-{1IHclp05N00L+^1u zICj)kK_D>+tW&qe!cm39`Nl>dV4N%p6IY(_w?J8(`jYrMjrpi3%l~-Rm?J%zh1}7>auD@adc4buicC(q71eN-F^B7W-tMkOjztS~* z{1WvuYAqo&M)^qDQtvl3$_CwTP-fGwbx((kk>D48Va!h$e+tRAg#Mk+P`PUWBT8uA!cy=XMW z*})$814NsPD#Rc2-7k;^5)A4;V9VbAP2o8&hR}@NIAvrPHFNJMkor}=h*K;Q?bGjT#$ac`3TKDbWU z)t(SQ&Dqe%_khWEMa0%_Ybsgow16SD_-qME9jFsoA<&2dsuXE4*}zmTah!0SqN81{ zSpRUD>NT}p*cQk{p#FBs7WU}G&C?eCNW8VPZ`WEo$;>-Q?sGIM7;we-|6&Vc_+J(+ zzfQ*E%S1|fDrvo^ZvV*pL!NZc|5yyLC@rTV2Uw0x3X?pXtNFM)wO^Yj;u9$iE+jjO zPv8%w`fr=F4Fo6FQdMQ`f|H)ItbAm_qkcx*FZgjvO7oT z3ZALvKr5t}>n@K7Lx%%HUCGzq*@|<7(j$#MR%?@eK=L zp^x+Q<}_BO6BYnod+u@V{^irqIWoiU&9FzoS1JvdE2HP}ldWg1caGb}>>IfsXP9nb zB&#LZ?AZWJc{nJq+Nag6DZ*g0@26j9QgrQD@>}sIozhT}Q682W4xUK`LIxx>J+Gq{ zK_qI#2YA1{KVq!@mzU}P(-IvG3E7P?!-p(kqkkkt@@(ye+I~pZh*27>m|-x}t9&ia zutHW`epcM`E^>hPdd<7F2zOmM6y?`LzxFqx@*(1Xz@voIcHN*WJp=nQthq@(YQc2- z->}(VHNN~m#WT*R^`dV6oD(4E^^DYWuZW;6reM~Om7kC-U6t2aEX%yZBm%yvi57nN zfIK7tep3+mqHid~4`+FD&A~FRBwh+X<+_lnH)FIWAvt*kYzgQg!sI?d?k zQQ6f8dD4Ot=i6UuBCFOybqnUkHR&Aym;4kNre=G2CWxgodutae>q!r-o+3Mm#Xq;fKt%PFdH|1GN(+P#;Ar9}fi;O-S%;@f_I+;-Bzk5B+e+kMQC zW<|L)FM*XFe!rfClf0fJR4-JIL0>=4D5|Uc55#!T&s^O#Uw$#;#=>(objectToGetKeysOf: TObject) => + Object.keys(objectToGetKeysOf) as (keyof TObject)[]; + +const stellarDependencies = { base: ['stellar-default-impl-macro'], fungible: ['stellar-fungible'], nonFungible: ['stellar-non-fungible'], pausable: ['stellar-pausable', 'stellar-pausable-macros'], upgradable: ['stellar-upgradeable', 'stellar-upgradeable-macros'], +} as const; + +const allStellarDependencies = getKeysOf(stellarDependencies); + +const allDependencies = { + ...stellarDependencies, soroban: ['soroban-sdk'], } as const; -const addDependenciesWith = (dependencyValue: string, dependenciesToAdd: (keyof typeof dependencies)[]) => +const addDependenciesWith = (dependencyValue: string, dependenciesToAdd: (keyof typeof allDependencies)[]) => dependenciesToAdd.reduce((addedDependency, dependencyName) => { - return `${addedDependency}${dependencies[dependencyName].map(cargoDependencies => `${cargoDependencies} = ${dependencyValue}\n`).join('')}`; + return `${addedDependency}${allDependencies[dependencyName].map(cargoDependencies => `${cargoDependencies} = ${dependencyValue}\n`).join('')}`; }, ''); -const getDependenciesToAdd = (opts: GenericOptions): (keyof typeof dependencies)[] => { - const dependenciesToAdd: (keyof typeof dependencies)[] = ['base']; - - if (opts.kind === 'Fungible') dependenciesToAdd.push('fungible'); - if (opts.kind === 'NonFungible') dependenciesToAdd.push('nonFungible'); - if (opts.pausable) dependenciesToAdd.push('pausable'); - if (opts.upgradeable) dependenciesToAdd.push('upgradable'); - - return dependenciesToAdd; -}; - const test = (c: Contract) => `#![cfg(test)] extern crate std; @@ -65,7 +63,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -${addDependenciesWith('{ workspace = true }', [...getDependenciesToAdd(opts), 'soroban'])} +${addDependenciesWith('{ workspace = true }', [...allStellarDependencies, 'soroban'])} [dev-dependencies] ${addDependenciesWith('{ workspace = true, features = ["testutils"] }', ['soroban'])} `; @@ -165,7 +163,7 @@ setup_environment() { cp Cargo.toml Cargo.toml.bak cat < deps.tmp -${addDependenciesWith(`{ git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "${contractsVersionTag}" }`, getDependenciesToAdd(opts))}${addDependenciesWith(`{ version = "${compatibleSorobanVersion}" }`, ['soroban'])} +${addDependenciesWith(`{ git = "https://github.com/OpenZeppelin/stellar-contracts", tag = "${contractsVersionTag}" }`, allStellarDependencies)}${addDependenciesWith(`{ version = "${compatibleSorobanVersion}" }`, ['soroban'])} EOF awk ' diff --git a/packages/mcp/README.md b/packages/mcp/README.md index 5da0c8afb..80b0da2d3 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -1,5 +1,7 @@ # OpenZeppelin Contracts Wizard MCP Server +[![NPM Package](https://img.shields.io/npm/v/@openzeppelin/wizard-mcp)](https://www.npmjs.com/package/@openzeppelin/wizard-mcp) + A Model Context Protocol (MCP) server that allows AI agents to generate smart contracts using the [OpenZeppelin Contracts Wizard](https://wizard.openzeppelin.com/). > [!WARNING] diff --git a/packages/mcp/src/stellar/schemas.ts b/packages/mcp/src/stellar/schemas.ts index 51fca4bc0..0d80599e6 100644 --- a/packages/mcp/src/stellar/schemas.ts +++ b/packages/mcp/src/stellar/schemas.ts @@ -27,6 +27,7 @@ export const commonSchema = { upgradeable: z.boolean().optional().describe(stellarCommonDescriptions.upgradeable), info: z .object({ + securityContact: z.string().optional().describe(infoDescriptions.securityContact), license: z.string().optional().describe(infoDescriptions.license), }) .optional() diff --git a/packages/mcp/src/stellar/tools/fungible.test.ts b/packages/mcp/src/stellar/tools/fungible.test.ts index 6b655f446..889707de8 100644 --- a/packages/mcp/src/stellar/tools/fungible.test.ts +++ b/packages/mcp/src/stellar/tools/fungible.test.ts @@ -50,6 +50,7 @@ test('all', async t => { access: 'ownable', info: { license: 'MIT', + securityContact: 'security@contact.com', }, }; assertHasAllSupportedFields(t, params); diff --git a/packages/mcp/src/stellar/tools/non-fungible.test.ts b/packages/mcp/src/stellar/tools/non-fungible.test.ts index ab20893e4..22464b5e9 100644 --- a/packages/mcp/src/stellar/tools/non-fungible.test.ts +++ b/packages/mcp/src/stellar/tools/non-fungible.test.ts @@ -52,6 +52,7 @@ test('all', async t => { access: 'ownable', info: { license: 'MIT', + securityContact: 'security@contact.com', }, }; assertHasAllSupportedFields(t, params); diff --git a/packages/mcp/src/stellar/tools/stablecoin.test.ts b/packages/mcp/src/stellar/tools/stablecoin.test.ts index 74e00a0f4..b338bca43 100644 --- a/packages/mcp/src/stellar/tools/stablecoin.test.ts +++ b/packages/mcp/src/stellar/tools/stablecoin.test.ts @@ -51,6 +51,7 @@ test('all', async t => { limitations: 'allowlist', info: { license: 'MIT', + securityContact: 'security@contact.com', }, }; assertHasAllSupportedFields(t, params); diff --git a/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts b/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts index c2681fdfc..6c030b1bc 100644 --- a/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts @@ -21,6 +21,11 @@ export const stellarCommonFunctionDescription = { type: 'object', description: infoDescriptions.info, properties: { + securityContact: { + type: 'string', + description: infoDescriptions.securityContact, + }, + license: { type: 'string', description: infoDescriptions.license, diff --git a/packages/ui/src/stellar/InfoSection.svelte b/packages/ui/src/stellar/InfoSection.svelte index adc17b756..238e1085c 100644 --- a/packages/ui/src/stellar/InfoSection.svelte +++ b/packages/ui/src/stellar/InfoSection.svelte @@ -1,6 +1,7 @@ @@ -13,6 +14,16 @@ + +