diff --git a/.changeset/ninety-readers-kneel.md b/.changeset/ninety-readers-kneel.md new file mode 100644 index 000000000..7e8c94ca7 --- /dev/null +++ b/.changeset/ninety-readers-kneel.md @@ -0,0 +1,7 @@ +--- +'@openzeppelin/wizard-stellar': minor +--- + +Add Stablecoin with Limitations and Access Control (ownable and roles). +- **Breaking changes**: + - Use OpenZeppelin Stellar Soroban Contracts v0.3.0 diff --git a/.changeset/proud-brooms-happen.md b/.changeset/proud-brooms-happen.md new file mode 100644 index 000000000..a71e6988e --- /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 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). diff --git a/packages/common/src/ai/descriptions/stellar.ts b/packages/common/src/ai/descriptions/stellar.ts index bf47b925d..18532c024 100644 --- a/packages/common/src/ai/descriptions/stellar.ts +++ b/packages/common/src/ai/descriptions/stellar.ts @@ -5,10 +5,13 @@ export const stellarPrompts = { Fungible: 'Make a fungible token per the Fungible Token Standard, compatible with SEP-41, similar to ERC-20.', NonFungible: 'Make a non-fungible token per the Non-Fungible Token Standard, compatible with SEP-50, similar to ERC-721.', + Stablecoin: 'Make a stablecoin that uses Fungible Token Standard, compatible with SEP-41.', }; export const stellarCommonDescriptions = { upgradeable: 'Whether the contract can be upgraded.', + access: + '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.', }; export const stellarFungibleDescriptions = { @@ -20,3 +23,8 @@ export const stellarNonFungibleDescriptions = { consecutive: 'To batch mint NFTs instead of minting them individually (sequential minting is mandatory).', sequential: 'Whether the IDs of the minted NFTs will be sequential.', }; + +export const stellarStablecoinDescriptions = { + limitations: 'Whether to restrict certain users from transferring tokens, either via allowing or blocking them.', + premint: 'The number of tokens to premint for the deployer.', +}; diff --git a/packages/core/stellar/src/add-pausable.ts b/packages/core/stellar/src/add-pausable.ts index fb029a23e..9b13c198b 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,12 @@ 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, { useMacro: true, role: 'pauser', caller: 'caller' }); + requireAccessControl(c, pausableTrait, functions.unpause, access, { + useMacro: true, + role: 'pauser', + caller: 'caller', + }); } const functions = defineFunctions({ @@ -31,10 +36,10 @@ const functions = defineFunctions({ }, pause: { args: [getSelfArg(), { name: 'caller', type: 'Address' }], - code: ['pausable::pause(e, &caller)'], + code: ['pausable::pause(e)'], }, unpause: { args: [getSelfArg(), { name: 'caller', type: 'Address' }], - code: ['pausable::unpause(e, &caller)'], + code: ['pausable::unpause(e)'], }, }); diff --git a/packages/core/stellar/src/add-upgradeable.ts b/packages/core/stellar/src/add-upgradeable.ts index eac50bd14..9329adda4 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,9 @@ 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, { + useMacro: false, + role: 'upgrader', + caller: 'operator', + }); } diff --git a/packages/core/stellar/src/api.ts b/packages/core/stellar/src/api.ts index 329d24b35..1ed40ab4d 100644 --- a/packages/core/stellar/src/api.ts +++ b/packages/core/stellar/src/api.ts @@ -1,8 +1,22 @@ import type { CommonContractOptions } from './common-options'; import type { FungibleOptions } from './fungible'; import type { NonFungibleOptions } from './non-fungible'; -import { printFungible, defaults as fungibledefaults } from './fungible'; -import { printNonFungible, defaults as nonFungibledefaults } from './non-fungible'; +import type { StablecoinOptions } from './stablecoin'; +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 { /** @@ -24,15 +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 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/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/contract.test.ts.md b/packages/core/stellar/src/contract.test.ts.md index cf69ec84c..595487430 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␊ #![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␊ #![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␊ #![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␊ #![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␊ #![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␊ #![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␊ #![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␊ #![no_std]␊ ␊ use another::library::{self as custom1, self as custom2, AnotherLibrary};␊ @@ -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␊ ␊ //! Some documentation␊ #![no_std]␊ @@ -160,7 +160,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␊ ␊ //! # Security␊ //!␊ @@ -176,7 +176,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␊ ␊ //! Some documentation␊ ␊ diff --git a/packages/core/stellar/src/contract.test.ts.snap b/packages/core/stellar/src/contract.test.ts.snap index 74265bac0..3a0e01156 100644 Binary files a/packages/core/stellar/src/contract.test.ts.snap and b/packages/core/stellar/src/contract.test.ts.snap differ diff --git a/packages/core/stellar/src/contract.ts b/packages/core/stellar/src/contract.ts index 5e9bf6411..05731075b 100644 --- a/packages/core/stellar/src/contract.ts +++ b/packages/core/stellar/src/contract.ts @@ -56,8 +56,8 @@ export interface BaseFunction { } export interface ContractFunction extends BaseFunction { + tags: string[]; codeBefore?: string[]; - tag?: string; } export interface Variable { @@ -177,6 +177,7 @@ export class ContractBuilder implements Contract { const contractFn: ContractFunction = { ...fn, pub: true, + tags: [], codeBefore: [], }; this.freeFunctionsMap.set(signature, contractFn); @@ -201,6 +202,7 @@ export class ContractBuilder implements Contract { // Otherwise, add the function const contractFn: ContractFunction = { ...fn, + tags: [], codeBefore: [], }; t.functions.push(contractFn); @@ -236,7 +238,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 { @@ -249,6 +251,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.test.ts b/packages/core/stellar/src/fungible.test.ts index 251efd2fc..8d105f82d 100644 --- a/packages/core/stellar/src/fungible.test.ts +++ b/packages/core/stellar/src/fungible.test.ts @@ -67,7 +67,11 @@ testFungible('fungible ownable', { access: 'ownable', }); -testFungible('fungible full', { +testFungible('fungible roles', { + access: 'roles', +}); + +testFungible('fungible full - ownable', { premint: '2000', access: 'ownable', burnable: true, @@ -75,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 dd2f19fbc..16c34ee57 100644 --- a/packages/core/stellar/src/fungible.test.ts.md +++ b/packages/core/stellar/src/fungible.test.ts.md @@ -9,11 +9,12 @@ 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␊ #![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);␊ - }␊ + type ContractType = Base;␊ ␊ - 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)␊ - }␊ }␊ ` @@ -70,11 +39,12 @@ 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␊ #![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)␊ - }␊ - ␊ - 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␊ //␊ ␊ + #[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 @@ -146,75 +77,41 @@ 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␊ #![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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## fungible burnable pausable @@ -251,75 +146,41 @@ 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␊ #![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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## fungible preminted @@ -373,11 +232,12 @@ 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␊ #![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)␊ - }␊ - ␊ - 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);␊ - }␊ + type ContractType = Base;␊ ␊ - 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)␊ - }␊ }␊ ` @@ -435,11 +263,12 @@ 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␊ #![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,62 +276,60 @@ 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)␊ - }␊ + type ContractType = Base;␊ ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ + }␊ + ` + +## fungible mintable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0␊ + #![no_std]␊ ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ + use soroban_sdk::{Address, contract, contractimpl, Env, String};␊ + use stellar_default_impl_macro::default_impl;␊ + use stellar_fungible::{Base, FungibleToken};␊ ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ + #[contract]␊ + pub struct MyToken;␊ ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ + #[contractimpl]␊ + impl MyToken {␊ + pub fn __constructor(e: &Env) {␊ + Base::set_metadata(e, 18, String::from_str(e, "MyToken"), String::from_str(e, "MTK"));␊ }␊ + }␊ ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + type ContractType = Base;␊ ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ }␊ ` -## fungible mintable +## fungible ownable > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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};␊ + use stellar_ownable::{self as ownable, Ownable};␊ ␊ #[contract]␊ pub struct MyToken;␊ @@ -510,205 +337,209 @@ 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)␊ - }␊ + 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)␊ - }␊ + //␊ + // Utils␊ + //␊ ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ + ` + +## fungible roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.0␊ + #![no_std]␊ ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ - }␊ + 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};␊ ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ - }␊ + #[contract]␊ + pub struct MyToken;␊ ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ + #[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);␊ }␊ + }␊ ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ - }␊ + #[default_impl]␊ + #[contractimpl]␊ + impl FungibleToken for MyToken {␊ + type ContractType = Base;␊ ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ - }␊ }␊ ␊ //␊ - // Extensions␊ + // Utils␊ //␊ ␊ + #[default_impl]␊ #[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);␊ - }␊ - }␊ + impl AccessControl for MyToken {}␊ ` -## fungible ownable +## fungible full - ownable > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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, 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 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, recipient: Address, owner: Address) {␊ + 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)␊ - }␊ + type ContractType = Base;␊ ␊ - fn balance(e: &Env, account: Address) -> i128 {␊ - fungible::balance(e, &account)␊ + #[when_not_paused]␊ + fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ + Self::ContractType::transfer(e, &from, &to, amount);␊ }␊ ␊ - fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {␊ - fungible::allowance(e, &owner, &spender)␊ + #[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);␊ }␊ + }␊ ␊ - fn transfer(e: &Env, from: Address, to: Address, amount: i128) {␊ - fungible::transfer(e, &from, &to, amount);␊ - }␊ + //␊ + // Extensions␊ + //␊ ␊ - fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {␊ - fungible::transfer_from(e, &spender, &from, &to, amount);␊ + #[contractimpl]␊ + impl FungibleBurnable for MyToken {␊ + #[when_not_paused]␊ + fn burn(e: &Env, from: Address, amount: i128) {␊ + Base::burn(e, &from, amount);␊ }␊ ␊ - fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {␊ - fungible::approve(e, &owner, &spender, amount, live_until_ledger);␊ + #[when_not_paused]␊ + fn burn_from(e: &Env, spender: Address, from: Address, amount: i128) {␊ + Base::burn_from(e, &spender, &from, amount);␊ }␊ + }␊ + ␊ + //␊ + // Utils␊ + //␊ ␊ - fn decimals(e: &Env) -> u32 {␊ - fungible::metadata::decimals(e)␊ + #[contractimpl]␊ + impl Pausable for MyToken {␊ + fn paused(e: &Env) -> bool {␊ + pausable::paused(e)␊ }␊ ␊ - fn name(e: &Env) -> String {␊ - fungible::metadata::name(e)␊ + #[only_owner]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e);␊ }␊ ␊ - fn symbol(e: &Env) -> String {␊ - fungible::metadata::symbol(e)␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` -## fungible full +## fungible full - roles > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.2.0␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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, Symbol};␊ + use stellar_access_control::{self as access_control, AccessControl};␊ + use stellar_access_control_macros::only_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;␊ ␊ - 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);␊ + 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"));␊ + }␊ + ␊ + #[only_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 {␊ - 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 +551,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 +570,20 @@ Generated by [AVA](https://avajs.dev). pausable::paused(e)␊ }␊ ␊ + #[only_role(caller, "pauser")]␊ 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);␊ + pausable::pause(e);␊ }␊ ␊ + #[only_role(caller, "pauser")]␊ 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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl AccessControl for MyToken {}␊ ` ## fungible full - complex name @@ -772,78 +591,48 @@ 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␊ #![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 +644,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 +663,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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[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 aa6df0d2d..1fa17ca7f 100644 Binary files a/packages/core/stellar/src/fungible.test.ts.snap and b/packages/core/stellar/src/fungible.test.ts.snap differ diff --git a/packages/core/stellar/src/fungible.ts b/packages/core/stellar/src/fungible.ts index aa091b0f7..21a507661 100644 --- a/packages/core/stellar/src/fungible.ts +++ b/packages/core/stellar/src/fungible.ts @@ -1,4 +1,3 @@ -import type { Contract } from './contract'; import { ContractBuilder } from './contract'; import type { Access } from './set-access-control'; import { requireAccessControl, setAccessControl } from './set-access-control'; @@ -21,7 +20,7 @@ export const defaults: Required = { 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; @@ -39,7 +38,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 +50,11 @@ function withDefaults(opts: FungibleOptions): Required { }; } -export function buildFungible(opts: FungibleOptions): Contract { +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); const allOpts = withDefaults(opts); @@ -86,43 +89,73 @@ 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); } } +function addMintable(c: ContractBuilder, access: Access, pausable: boolean) { + switch (access) { + case false: + break; + case 'ownable': { + c.addFreeFunction(functions.mint); + + requireAccessControl(c, undefined, functions.mint, access); + + if (pausable) { + c.addFunctionTag(functions.mint, 'when_not_paused'); + } + break; + } + case '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'); + } + break; + } + default: { + const _: never = access; + throw new Error('Unknown value for `access`'); + } + } +} + function addBurnable(c: ContractBuilder, pausable: boolean) { c.addUseClause('stellar_fungible', 'burnable::FungibleBurnable'); c.addUseClause('soroban_sdk', 'Address'); @@ -134,13 +167,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 +195,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});`); } } @@ -206,41 +243,22 @@ export function getInitialSupply(premint: string, decimals: number): string { return result; } -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); - - requireAccessControl(c, fungibleMintableTrait, functions.mint, access); - - if (pausable) { - c.addFunctionTag(functions.mint, 'when_not_paused', fungibleMintableTrait); - } -} - -const functions = defineFunctions({ +export const functions = defineFunctions({ // Token Functions 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 +267,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 +277,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 +287,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: ['Base::burn(e, &from, amount)'], }, burn_from: { args: [ @@ -299,10 +317,20 @@ const functions = defineFunctions({ { name: 'from', type: 'Address' }, { name: 'amount', type: 'i128' }, ], - code: ['fungible::burnable::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' }], - code: ['fungible::mintable::mint(e, &account, amount);'], + 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/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..42717eff5 --- /dev/null +++ b/packages/core/stellar/src/generate/stablecoin.ts @@ -0,0 +1,23 @@ +import { limitationsOptions, 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, + limitations: limitationsOptions, + 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 020d7263c..a42210e1c 100644 --- a/packages/core/stellar/src/index.ts +++ b/packages/core/stellar/src/index.ts @@ -23,6 +23,8 @@ export { contractsVersion, contractsVersionTag, compatibleContractsSemver } from export { fungible } from './api'; export { nonFungible } from './api'; +export { stablecoin } from './api'; export type { FungibleOptions } from './fungible'; export type { NonFungibleOptions } from './non-fungible'; +export type { StablecoinOptions } from './stablecoin'; 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/non-fungible.test.ts.md b/packages/core/stellar/src/non-fungible.test.ts.md index 00d145d86..88882b29c 100644 --- a/packages/core/stellar/src/non-fungible.test.ts.md +++ b/packages/core/stellar/src/non-fungible.test.ts.md @@ -9,10 +9,11 @@ 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␊ #![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)␊ - }␊ }␊ ` @@ -86,10 +42,11 @@ 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␊ #![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 @@ -178,44 +83,35 @@ 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␊ #![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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible burnable pausable @@ -299,44 +155,35 @@ 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␊ #![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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible mintable @@ -437,13 +244,14 @@ 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␊ #![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 @@ -523,13 +293,13 @@ 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␊ #![no_std]␊ ␊ 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]␊ @@ -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)␊ - }␊ }␊ ␊ //␊ @@ -611,16 +336,16 @@ 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␊ #![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␊ + Base, consecutive::{NonFungibleConsecutive, Consecutive}, 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 +353,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 +379,14 @@ Generated by [AVA](https://avajs.dev). ␊ #[contractimpl]␊ impl NonFungibleConsecutive for MyToken {}␊ + ␊ + //␊ + // Utils␊ + //␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible consecutive burnable @@ -707,16 +394,17 @@ 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␊ #![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␊ + 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 +412,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 +437,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 @@ -814,54 +457,43 @@ 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␊ #![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␊ + Base, consecutive::{NonFungibleConsecutive, Consecutive}, 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 +504,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 +523,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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible consecutive burnable pausable @@ -952,54 +544,44 @@ 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␊ #![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␊ + 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 +592,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 +624,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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible sequential @@ -1103,10 +645,11 @@ 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␊ #![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 +658,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)␊ - }␊ }␊ ` @@ -1180,91 +678,37 @@ 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␊ #![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 +717,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 @@ -1287,58 +731,47 @@ 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␊ #![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␊ + Base, burnable::NonFungibleBurnable, 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 +782,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 +811,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 +821,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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyToken {}␊ ` ## non-fungible - complex name @@ -1453,44 +842,35 @@ 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␊ #![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 +881,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 +910,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);␊ + pausable::pause(e);␊ }␊ ␊ + #[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);␊ + pausable::unpause(e);␊ }␊ }␊ + ␊ + #[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 8f39a6af5..9282afe58 100644 Binary files a/packages/core/stellar/src/non-fungible.test.ts.snap and b/packages/core/stellar/src/non-fungible.test.ts.snap differ diff --git a/packages/core/stellar/src/non-fungible.ts b/packages/core/stellar/src/non-fungible.ts index 9984a6f99..3776ab4a4 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 || opts.consecutive === true; +} + export function buildNonFungible(opts: NonFungibleOptions): Contract { const c = new ContractBuilder(opts.name); @@ -117,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'); @@ -133,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); } } @@ -169,20 +167,24 @@ 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); } } 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', @@ -193,23 +195,10 @@ 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) { c.addUseClause('stellar_non_fungible', 'consecutive::{NonFungibleConsecutive, Consecutive}'); - c.addUseClause('stellar_non_fungible', 'ContractOverrides'); const nonFungibleConsecutiveTrait = { traitName: 'NonFungibleConsecutive', @@ -222,48 +211,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({ @@ -347,10 +336,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({ @@ -375,33 +380,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/core/stellar/src/print.ts b/packages/core/stellar/src/print.ts index 7b08fe702..ce0058440 100644 --- a/packages/core/stellar/src/print.ts +++ b/packages/core/stellar/src/print.ts @@ -248,7 +248,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[] { @@ -289,7 +289,7 @@ function printConstructor(contract: Contract): Lines[] { true, head, args.map(a => printArgument(a)), - undefined, + [], undefined, undefined, body, @@ -306,15 +306,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 0242b2dd9..7851a576a 100644 --- a/packages/core/stellar/src/set-access-control.ts +++ b/packages/core/stellar/src/set-access-control.ts @@ -1,9 +1,14 @@ 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 AccessProps = { + useMacro: boolean; + caller?: string; + role?: string; +}; /** * Sets access control for the contract via constructor args. @@ -15,14 +20,40 @@ 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.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('e.storage().instance().set(&OWNER, &owner);'); + 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`'); @@ -32,15 +63,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, - caller?: string, + accessProps: AccessProps = { useMacro: true }, ): void { if (access === false) { access = DEFAULT_ACCESS_CONTROL; @@ -49,19 +78,43 @@ 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_macro', 'only_owner'); + + if (accessProps.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; + } + 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', 'only_role'); + c.addFunctionTag(fn, `only_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: { diff --git a/packages/core/stellar/src/stablecoin.test.ts b/packages/core/stellar/src/stablecoin.test.ts new file mode 100644 index 000000000..96feae9ff --- /dev/null +++ b/packages/core/stellar/src/stablecoin.test.ts @@ -0,0 +1,141 @@ +import test from 'ava'; + +import type { StablecoinOptions } from './stablecoin'; +import { buildStablecoin } from './stablecoin'; +import { printContract } from './print'; + +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 - 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, +}); + +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..a5683de44 --- /dev/null +++ b/packages/core/stellar/src/stablecoin.test.ts.md @@ -0,0 +1,1084 @@ +# 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.3.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.3.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.3.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);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin burnable pausable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin preminted + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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.3.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.3.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.3.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.3.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.3.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.3.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 - ownable, allowlist + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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, 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 = 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)␊ + }␊ + ␊ + #[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) {␊ + 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);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl Ownable for MyStablecoin {}␊ + ` + +## stablecoin full - ownable, blocklist + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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}, 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);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ + }␊ + }␊ + ␊ + #[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␊ + #![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::only_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"));␊ + }␊ + ␊ + #[only_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)␊ + }␊ + ␊ + #[only_role(operator, "manager")]␊ + fn allow_user(e: &Env, user: Address, operator: Address) {␊ + AllowList::allow_user(e, &user);␊ + }␊ + ␊ + #[only_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)␊ + }␊ + ␊ + #[only_role(caller, "pauser")]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e);␊ + }␊ + ␊ + #[only_role(caller, "pauser")]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ + }␊ + }␊ + ␊ + #[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␊ + #![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::only_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"));␊ + }␊ + ␊ + #[only_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)␊ + }␊ + ␊ + #[only_role(operator, "manager")]␊ + fn block_user(e: &Env, user: Address, operator: Address) {␊ + BlockList::block_user(e, &user);␊ + }␊ + ␊ + #[only_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)␊ + }␊ + ␊ + #[only_role(caller, "pauser")]␊ + fn pause(e: &Env, caller: Address) {␊ + pausable::pause(e);␊ + }␊ + ␊ + #[only_role(caller, "pauser")]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ + }␊ + }␊ + ␊ + #[default_impl]␊ + #[contractimpl]␊ + impl AccessControl for MyStablecoin {}␊ + ` + +## stablecoin full - complex name + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Stellar Soroban Contracts ^0.3.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);␊ + }␊ + ␊ + #[only_owner]␊ + fn unpause(e: &Env, caller: Address) {␊ + pausable::unpause(e);␊ + }␊ + }␊ + ␊ + #[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 000000000..2d5dcfd71 Binary files /dev/null and b/packages/core/stellar/src/stablecoin.test.ts.snap differ diff --git a/packages/core/stellar/src/stablecoin.ts b/packages/core/stellar/src/stablecoin.ts new file mode 100644 index 000000000..dab64b990 --- /dev/null +++ b/packages/core/stellar/src/stablecoin.ts @@ -0,0 +1,124 @@ +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, + isAccessControlRequired as fungibleIsAccessControlRequired, +} from './fungible'; +import { requireAccessControl, type Access } from './set-access-control'; + +export const defaults: Required = { + ...fungibleDefaults, + name: 'MyStablecoin', + symbol: 'MST', + limitations: false, +} as const; + +export const limitationsOptions = [false, 'allowlist', 'blocklist'] 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 isAccessControlRequired(opts: Partial): boolean { + return fungibleIsAccessControlRequired(opts) || opts.limitations !== false; +} + +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: '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); + + const accessProps = { + useMacro: true, + role: 'manager', + caller: 'operator', + }; + + c.addTraitFunction(limitationsTrait, addFn); + requireAccessControl(c, limitationsTrait, addFn, access, accessProps); + + c.addTraitFunction(limitationsTrait, removeFn); + requireAccessControl(c, limitationsTrait, removeFn, access, accessProps); +} + +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/core/stellar/src/utils/define-functions.ts b/packages/core/stellar/src/utils/define-functions.ts index 3c89e6c76..ac37dca52 100644 --- a/packages/core/stellar/src/utils/define-functions.ts +++ b/packages/core/stellar/src/utils/define-functions.ts @@ -1,9 +1,16 @@ import type { BaseFunction } from '../contract'; -type ImplicitNameFunction = Omit; +type OverridableImplicitNameFunction = Omit & { name?: string }; -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)])); +export function defineFunctions(fns: Record): Record { + return Object.fromEntries( + Object.entries(fns).map(([implicitName, fn]) => [ + implicitName, + Object.assign({ name: fn.name ?? implicitName }, fn), + ]), + ); } diff --git a/packages/core/stellar/src/utils/version.ts b/packages/core/stellar/src/utils/version.ts index 323307f99..265a5140a 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'; 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'; /** * The Soroban version for which compilation and testing have passing tests 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 da692e0c2..000000000 --- a/packages/core/stellar/src/zip-scaffold.test.ts.md +++ /dev/null @@ -1,10607 +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 }␊ - 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 deleted file mode 100644 index 5d2feb190..000000000 Binary files a/packages/core/stellar/src/zip-scaffold.test.ts.snap and /dev/null differ diff --git a/packages/mcp/README.md b/packages/mcp/README.md index 9fd41f6dd..5466dc156 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -15,7 +15,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 | diff --git a/packages/mcp/src/stellar/schemas.ts b/packages/mcp/src/stellar/schemas.ts index 34ac242a6..0d80599e6 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({ @@ -41,6 +44,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 7f0331963..889707de8 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', securityContact: 'security@contact.com', 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 63ba612f5..22464b5e9 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', securityContact: 'security@contact.com', 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..b338bca43 --- /dev/null +++ b/packages/mcp/src/stellar/tools/stablecoin.test.ts @@ -0,0 +1,59 @@ +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', + securityContact: 'security@contact.com', + }, + }; + 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)), + }, + ], + }; + }, + ); +} 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 3312a2bfb..6c030b1bc 100644 --- a/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/stellar-shared.ts @@ -5,10 +5,11 @@ import { stellarCommonDescriptions } from '../../../../common/src/ai/description 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: stellarCommonDescriptions.access, }, upgradeable: { diff --git a/packages/ui/api/ai-assistant/function-definitions/stellar.ts b/packages/ui/api/ai-assistant/function-definitions/stellar.ts index 26bd50dc7..61b415283 100644 --- a/packages/ui/api/ai-assistant/function-definitions/stellar.ts +++ b/packages/ui/api/ai-assistant/function-definitions/stellar.ts @@ -6,6 +6,7 @@ import { stellarCommonDescriptions, stellarFungibleDescriptions, stellarNonFungibleDescriptions, + stellarStablecoinDescriptions, } from '../../../../common/src/ai/descriptions/stellar.ts'; export const stellarFungibleAIFunctionDefinition = { @@ -34,6 +35,42 @@ export const stellarFungibleAIFunctionDefinition = { }, } as const satisfies AiFunctionDefinition<'stellar', 'Fungible'>; +export const stellarStablecoinAIFunctionDefinition = { + name: 'Stablecoin', + description: stellarPrompts.Stablecoin, + 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: stellarStablecoinDescriptions.limitations, + }, + premint: { + type: 'string', + description: stellarStablecoinDescriptions.premint, + }, + upgradeable: { + type: 'boolean', + description: stellarCommonDescriptions.upgradeable, + }, + }, + required: ['name', 'symbol'], + additionalProperties: false, + }, +} as const satisfies AiFunctionDefinition<'stellar', 'Stablecoin'>; + export const stellarNonFungibleAIFunctionDefinition = { name: 'NonFungible', description: stellarPrompts.NonFungible, diff --git a/packages/ui/api/ai-assistant/types/languages.ts b/packages/ui/api/ai-assistant/types/languages.ts index 4197278ce..076b08e0f 100644 --- a/packages/ui/api/ai-assistant/types/languages.ts +++ b/packages/ui/api/ai-assistant/types/languages.ts @@ -12,8 +12,7 @@ export type { RoyaltyInfoOptions as CairoAlphaRoyaltyInfoOptions } from '../../. //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 StellarCommonContractOptions = Omit & { upgradeable?: false; }; // Stylus @@ -31,9 +30,10 @@ export type LanguagesContractsOptions = { }; cairo: CairoKindedOptions; cairoAlpha: CairoAlphaKindedOptions; - stellar: Omit & { + stellar: Omit & { Fungible: StellarKindedOptions['Fungible'] & StellarCommonContractOptions; NonFungible: StellarKindedOptions['NonFungible'] & StellarCommonContractOptions; + Stablecoin: StellarKindedOptions['Stablecoin'] & StellarCommonContractOptions; }; stylus: Omit & { ERC20: StylusKindedOptions['ERC20'] & StylusCommonContractOptions; 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/App.svelte b/packages/ui/src/stellar/App.svelte index baa704f54..27caf9543 100644 --- a/packages/ui/src/stellar/App.svelte +++ b/packages/ui/src/stellar/App.svelte @@ -7,6 +7,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'; @@ -121,6 +122,9 @@ case 'Fungible': opts.premint = initialOpts.premint ?? opts.premint; break; + case 'Stablecoin': + opts.premint = initialOpts.premint ?? opts.premint; + break; case 'NonFungible': break; } @@ -188,6 +192,7 @@ + @@ -251,6 +256,9 @@
+
+ +
diff --git a/packages/ui/src/stellar/FungibleControls.svelte b/packages/ui/src/stellar/FungibleControls.svelte
index 317610de3..049766612 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..322e6845e 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 new file mode 100644 index 000000000..392c37a46 --- /dev/null +++ b/packages/ui/src/stellar/StablecoinControls.svelte @@ -0,0 +1,102 @@ + + +
+

Settings

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

Features

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