Tutorial on end-to-end testing of transferFrom user flow
Objectives: Install the CLI and setup environment Successfully upload your first smart contract to Workbench. Visually design an end-to-end test scenario. Generate the state space and a test suite. Analyze and validate contract behavior. Export a test case to Hardhat and Foundry.
Run the init command from the main folder of your Foundry or Hardhat project to create the state-space.toml config file.
state-space init
It should look like this:
state-space.toml
# Sets the name of the project. ## The project is created if it does not already exist.project = "Project MyToken"# If using State Space in a team, specify the team identifier.# By default, your personal account will be used.team = "my-team"# Sets the primary framework to use.# # When setting a framework, contract sources and solc settings are # automatically used when pushing code to State Space.# # Optional. Supports "foundry" or "hardhat".framework = "hardhat"# Creates a new Workbench projectworkbench.new = "MyToken"# Specify paths to solc json output files.# If using a framework, this usually isn't necessary.# artifacts = []# Defines a deployment# # Deployments are a set of concrete transactions and accounts. # They are used as initial states when exploring your state space.# # Deployments are recorded by running a script against a local EVM.# Deployments are currently only supported when using a framework.# [deployment.Main]# Command is a list of strings representing the command to run when # recording this deployment. Ensure that your RPC endpoint points to# localhost:8545.# command = ["forge", "script", ...]# When using Foundry scripts, the scripts property may be used as a # shorthand alternative to the command property.# The RPC url does not need to be specified when using this form.# scripts = [# "name of or path to Foundry script", # ]# When using Hardhat's Ignition, the modules property may be used as a # shorthand alternative to the command property.# The RPC url does not need to be specified when using this form.# modules = [# "path to Ignition module", # ]# Specify the number of externally owned accounts (wallets) to use# in this deployment# accounts = 3
Authenticate using the same credentials you created in Step 1. After you authenticate, you will see your project appear in the browser Workbench dasboard.
3
View in Workbench
Use the browser to login to State Space, and select the My Contract Workbench project we just uploaded.Expand the folder tree to view your code as visual interactive components.Each component represents an externally visible function, with the following identifiers:Blue = CALL - function call
Purple = VIEW - view function
Green = CREATE - constructor
Gray = DEPLOY - deployment script
4
Design a test scenario
Build an end-to-end test scenario for the transferFrom(...) flow using the following sequence of transactions:Deployment → constructor(...) → approve(...) → transferFrom(...)
Since our project did not use a custom deployment script, we can use the provided deployment template to quickly setup a few EOAs.
From the bottom of the folder tree, drag an Empty Deployment component to the canvas and rename it to “ERC20 Deployment”.
Reduce the EOA count to two (2) by deleting Claire (keeping Alice and Bob). You can add more EOA accounts by selecting the + icon from inside the node.
To leverage custom deployment scripts as part of your testing flow, see the CLI reference or contact us for help.
Transaction and function call parameters can be set to behave as symbolic values or concrete inputs.When set to symbolic, State Space will automatically calculate the possible unique input values that will lead to an undiscovered path.Learn more about how structured dynamic inputs are generated.
Update the parameters so that:
Alice deploys the contract
Alice approves Bob as the _spender
Bob calls transferFrom(...)
Leave all other fields blank to act symbolically.It should look like this:
transferFrom workflow with symbolic and concrete parameters
5
Explore the state space
Select the icon to start.Discovered execution paths will appear dynamically during execution. Overall coverage metrics will be displayed once the state space exploration is completed.Contact us if you experience any issues.Upon completion, you should see a total 39 test cases (execution paths) with a 37% health score.
Health scores are a function of line, branch, and functional coverage metrics.
For a more in-depth coverage analysis, open the coverage view in a new tab by selecting the run number under “Run History”. Select your contract on the left navigation menu and view the covered lines under the source tab.Feel free to experiement with different parameter settings and sequences to analyze behavior and maximize coverage.
State Space explosion can easily occur with fully symbolic parameters and long sequences. We recommend constraining your analysis to your test objective to get meaningful, digestable results. If you accidentally encounter this, right click the active run (in Run History) to cancel the exploration run.
6
Analyze and validate results
Scroll through and locate the test case where:
Bob approves Aice to spend an allowance of uint MAX tokens, AND
Alice successfully transfers 1 token from Bob's account.
It should look like this:
Successful transaction
Transaction highlighted with green indicates a successful transaction occurred. Transactions ending with a red highlight indicate a reverting path.Here is an example of a reverted execution path (expected behavior) where State Space autonomously tested a transferFrom amount greater than the umax spending allowance:
Mark states as “Valid” by clicking through the shield icon:
Green shield: Valid state
Red shield: Invalid state
Grey shield: Not reviewed
Each validation is tracked in the History tab.
Multiple reviewers with can collaborate validating state values.
7
Exporting to Hardhat or Foundry
Select the “Export Test” button, and choose your framework (Hardhat or Foundry) to copy or download the test.Check the “Events” box to include assertions for event emissions.
Export Test Case to Foundry or Hardhat
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"State Space","description":"Test Generation powered by Symbolic Execution for EVM Smart Contracts.","colors":{"primary":"#0D9373","light":"#07C983","dark":"#0D9373"},"logo":{"light":"https://mintlify.s3.us-west-1.amazonaws.com/statespace/images/LogoLightMode.svg","dark":"https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/LogoDarkMode.svg?fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=3ac4cd5897f508af8dac6d66c91fac87"},"favicon":"/favicon.svg","appearance":{"default":"dark","strict":false},"background":{"decoration":"grid"},"navbar":{"links":[{"label":"Contact Us","href":"mailto:contact@state.space"}],"primary":{"type":"button","label":"Login to Workbench","href":"https://state.space/start"}},"footer":{"socials":{"x":"https://x.com/state_space_sys","github":"https://github.com/state-space","linkedin":"https://www.linkedin.com/company/state-space"}},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"statespace","hasAudioNarration":false,"actualSubdomain":"statespace","customDomains":["docs.state.space"],"gitSource":{"type":"github","owner":"state-space","repo":"docs","deployBranch":"master","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6a21a76732881c474e3234cb","collectionId":"6d452d02-3288-4f11-8197-fa09acf9e441","schemaVersion":"v2"},"entitlements":{},"buildId":"6a21a76732881c474e3234cb:success","clientVersion":"0.0.4045","searchFilterCounts":[{"language":"default","tag":"Documentation","count":11},{"language":"default","tag":"State Space CLI","count":4},{"language":"default","tag":"Getting Started","count":3},{"language":"default","tag":"Changelog","count":2},{"language":"default","tag":"Overview","count":2},{"language":"default","tag":"Tutorials","count":2}]},"assistantConfig":{},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"docs/tutorials/erc20-transferfrom","slug":"/docs/tutorials/erc20-transferfrom","pageMetadata":{"title":"ERC20: End-to-End Testing of transferFrom","sidebarTitle":"ERC20: End-to-End Testing of transferFrom","icon":"arrow-right-arrow-left","description":"Tutorial on end-to-end testing of transferFrom user flow","href":"/docs/tutorials/erc20-transferfrom"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Tutorial on end-to-end testing of transferFrom user flow\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,br:`br`,code:`code`,hr:`hr`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,CodeGroup,Frame,Heading,Icon,Info,Note,OptimizedImage,Step,Steps,Tip,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!CodeGroup)_missingMdxReference(`CodeGroup`,true);if(!Frame)_missingMdxReference(`Frame`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Icon)_missingMdxReference(`Icon`,true);if(!Info)_missingMdxReference(`Info`,true);if(!Note)_missingMdxReference(`Note`,true);if(!OptimizedImage)_missingMdxReference(`OptimizedImage`,true);if(!Step)_missingMdxReference(`Step`,true);if(!Steps)_missingMdxReference(`Steps`,true);if(!Tip)_missingMdxReference(`Tip`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Frame,{children:_jsx(OptimizedImage,{src:`https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/Quickstart_Header.png?fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=2725b87cdc2048af10ff6677aca7bf93`,\"data-og-width\":`4014`,width:`4014`,\"data-og-height\":`2320`,height:`2320`,\"data-path\":`media/images/Quickstart_Header.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/Quickstart_Header.png?w=280\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=59e4347be10de754b7b4d7419f9e1d5a 280w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/Quickstart_Header.png?w=560\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=5fd00bb7147f63f1e0eb2fbc959bbc19 560w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/Quickstart_Header.png?w=840\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=f7b1507c597bd2bd0875c3f6b018e961 840w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/Quickstart_Header.png?w=1100\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=a0e591a81609260eddbf009e6844651d 1100w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/Quickstart_Header.png?w=1650\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=6f07453a22276a0e11f9acfa9a078033 1650w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/Quickstart_Header.png?w=2500\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=7de1bc6fa5771be0c0b97a07620f982f 2500w`})}),`\n`,_jsx(`br`,{}),`\n`,_jsx(_components.p,{children:_jsx(_components.strong,{children:`Objectives:`})}),`\n`,_jsxs(_components.p,{children:[_jsx(Icon,{icon:`check`,iconType:`solid`}),` Install the CLI and setup environment`,_jsx(_components.br,{}),`\n`,_jsx(Icon,{icon:`check`,iconType:`solid`}),` Successfully upload your first smart contract to Workbench.`,_jsx(_components.br,{}),`\n`,_jsx(Icon,{icon:`check`,iconType:`solid`}),` Visually design an end-to-end test scenario.`,_jsx(_components.br,{}),`\n`,_jsx(Icon,{icon:`check`,iconType:`solid`}),` Generate the state space and a test suite.`,_jsx(_components.br,{}),`\n`,_jsx(Icon,{icon:`check`,iconType:`solid`}),` Analyze and validate contract behavior.`,_jsx(_components.br,{}),`\n`,_jsx(Icon,{icon:`check`,iconType:`solid`}),` Export a test case to Hardhat and Foundry.`]}),`\n`,_jsxs(Steps,{titleSize:`h2`,children:[_jsxs(Step,{title:`Setup`,id:`setup`,children:[_jsxs(_components.p,{children:[`Create an account `,_jsx(_components.a,{href:`https://app.state.space/sign-up`,children:`here`}),`.`]}),_jsx(Heading,{level:`3`,id:`install-state-space-cli`,children:`Install State Space CLI:`}),_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`brew`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` install`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` state-space/state-space/cli`})]}),`\n`]})})}),_jsx(Heading,{level:`3`,id:`download-the-erc20-example-from-our-github-repo`,children:`Download the ERC20 example from our Github repo`}),_jsx(_components.p,{children:`For this tutorial we’ll use an unmodified ERC20 project from OpenZeppelin.`}),_jsx(_components.p,{children:`Clone the repo based on your framework preference.`}),_jsxs(CodeGroup,{children:[_jsx(CodeBlock,{filename:`Foundry`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`git`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` clone`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` https://github.com/state-space/public-examples.git`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026\u0026 `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`cd`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` public-examples/OpenZeppelinERC20_Foundry/`})]}),`\n`]})})}),_jsx(CodeBlock,{filename:`Hardhat`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`git`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` clone`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` https://github.com/state-space/public-examples.git`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026\u0026 `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`cd`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` public-examples/OpenZeppelinERC20_Hardhat/`})]}),`\n`]})})})]}),_jsx(Tip,{children:`If you are using your own project, make sure your code successfully compiles.`}),_jsx(_components.hr,{})]}),_jsxs(Step,{title:`Configure and Deploy`,id:`configure-and-deploy`,children:[_jsx(Heading,{level:`3`,id:`initialize-project`,children:`Initialize project`}),_jsxs(_components.p,{children:[`Run the `,_jsx(_components.code,{children:`init`}),` command from the main folder of your Foundry or Hardhat project to create the `,_jsx(_components.code,{children:`state-space.toml`}),` config file.`]}),_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`state-space`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` init`})]}),`\n`]})})}),_jsx(_components.p,{children:`It should look like this:`}),_jsx(CodeBlock,{filename:`state-space.toml`,expandable:`true`,numberOfLines:`56`,language:`toml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`toml`,children:_jsxs(_components.code,{language:`toml`,numberOfLines:`56`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Sets the name of the project. `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`#`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# The project is created if it does not already exist.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`project`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` = `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"Project MyToken\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# If using State Space in a team, specify the team identifier.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# By default, your personal account will be used.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`team`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` = `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"my-team\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Sets the primary framework to use.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# When setting a framework, contract sources and solc settings are `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# automatically used when pushing code to State Space.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Optional. Supports \"foundry\" or \"hardhat\".`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`framework`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` = `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"hardhat\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Creates a new Workbench project`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`workbench.new`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` = `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"MyToken\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Specify paths to solc json output files.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# If using a framework, this usually isn't necessary.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# artifacts = []`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Defines a deployment`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Deployments are a set of concrete transactions and accounts. `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# They are used as initial states when exploring your state space.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Deployments are recorded by running a script against a local EVM.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Deployments are currently only supported when using a framework.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# [deployment.Main]`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Command is a list of strings representing the command to run when `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# recording this deployment. Ensure that your RPC endpoint points to`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# localhost:8545.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# command = [\"forge\", \"script\", ...]`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# When using Foundry scripts, the scripts property may be used as a `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# shorthand alternative to the command property.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# The RPC url does not need to be specified when using this form.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# scripts = [`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# \"name of or path to Foundry script\", `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# ]`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# When using Hardhat's Ignition, the modules property may be used as a `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# shorthand alternative to the command property.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# The RPC url does not need to be specified when using this form.`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# modules = [`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# \"path to Ignition module\", `})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# ]`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# Specify the number of externally owned accounts (wallets) to use`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# in this deployment`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# accounts = 3 `})}),`\n`]})})}),_jsx(Heading,{level:`3`,id:`update-configuration`,children:`Update configuration`}),_jsxs(_components.p,{children:[`Add the `,_jsx(_components.code,{children:`workbench.new`}),` property to automatically create a Workbench project upon submission. Your settings should like this:`]}),_jsxs(CodeGroup,{children:[_jsx(CodeBlock,{filename:`Foundry`,numberOfLines:`4`,language:`toml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`toml`,children:_jsxs(_components.code,{language:`toml`,numberOfLines:`4`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# state-space.toml`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`project`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"MyToken\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` `})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`framework`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"foundry\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` `})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`workbench.new`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"MyToken\"`})]}),`\n`]})})}),_jsx(CodeBlock,{filename:`Hardhat`,numberOfLines:`4`,language:`toml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`toml`,children:_jsxs(_components.code,{language:`toml`,numberOfLines:`4`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# state-space.toml`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`project`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"MyToken\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` `})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`framework`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"hardhat\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` `})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`workbench.new`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"MyToken\"`})]}),`\n`]})})})]}),_jsx(Heading,{level:`3`,id:`deploy-to-state-space`,children:`Deploy to State Space`}),_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# execute command in terminal`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`state-space`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` push`})]}),`\n`]})})}),_jsx(_components.p,{children:`Authenticate using the same credentials you created in Step 1. After you authenticate, you will see your project appear in the browser Workbench dasboard.`}),_jsx(_components.hr,{})]}),_jsxs(Step,{title:`View in Workbench`,id:`view-in-workbench`,children:[_jsxs(_components.p,{children:[`Use the browser to `,_jsx(_components.a,{href:`https://state.space/me`,children:`login`}),` to State Space, and select the `,_jsx(_components.code,{children:`My Contract`}),` Workbench project we just uploaded.`]}),_jsx(_components.p,{children:`Expand the folder tree to view your code as visual interactive components.`}),_jsx(_components.p,{children:`Each component represents an externally visible function, with the following identifiers:`}),_jsxs(_components.p,{children:[`Blue = `,_jsx(_components.code,{children:`CALL`}),` - function call`,_jsx(_components.br,{}),`\n`,`Purple = `,_jsx(_components.code,{children:`VIEW`}),` - view function`,_jsx(_components.br,{}),`\n`,`Green = `,_jsx(_components.code,{children:`CREATE`}),` - constructor`,_jsx(_components.br,{}),`\n`,`Gray = `,_jsx(_components.code,{children:`DEPLOY`}),` - deployment script`]}),_jsx(Frame,{children:_jsx(OptimizedImage,{src:`https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/WorkbenchFolderTree.png?fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=2175ffa3b4e30806432d400d0258a380`,\"data-og-width\":`1007`,width:`1007`,\"data-og-height\":`994`,height:`994`,\"data-path\":`media/images/WorkbenchFolderTree.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/WorkbenchFolderTree.png?w=280\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=ad619f2c575fac46e13a27aa3e1ea64c 280w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/WorkbenchFolderTree.png?w=560\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=4dd6de455e83c0b846a1cc4ab02e2c1e 560w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/WorkbenchFolderTree.png?w=840\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=434b8eef8236b6e8a69d996e2f0253d6 840w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/WorkbenchFolderTree.png?w=1100\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=a81f01dfe9193ff25f7f82147455dd92 1100w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/WorkbenchFolderTree.png?w=1650\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=0518cb0a5b725e64f997758fef23091f 1650w, https://mintcdn.com/statespace/1PV0vXIcGni_BZ3w/media/images/WorkbenchFolderTree.png?w=2500\u0026fit=max\u0026auto=format\u0026n=1PV0vXIcGni_BZ3w\u0026q=85\u0026s=64cc60310cecc8210b71a9e851f39592 2500w`})}),_jsx(_components.hr,{})]}),_jsxs(Step,{title:`Design a test scenario`,id:`design-a-test-scenario`,children:[_jsxs(_components.p,{children:[`Build an end-to-end test scenario for the `,_jsx(_components.a,{href:`https://eips.ethereum.org/EIPS/eip-20#transferfrom`,children:_jsx(_components.code,{children:`transferFrom(...)`})}),` flow using the following sequence of transactions:`]}),_jsxs(_components.p,{children:[_jsx(_components.code,{children:`Deployment`}),` → `,_jsx(_components.code,{children:`constructor(...)`}),` → `,_jsx(_components.code,{children:`approve(...)`}),` → `,_jsx(_components.code,{children:`transferFrom(...)`})]}),_jsx(Heading,{level:`3`,id:`configure-a-basic-deployment`,children:`Configure a basic deployment`}),_jsx(_components.p,{children:`Since our project did not use a custom deployment script, we can use the provided deployment template to quickly setup a few EOAs.`}),_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[`From the bottom of the folder tree, drag an `,_jsx(_components.code,{children:`Empty Deployment`}),` component to the canvas and rename it to “ERC20 Deployment”.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[`Reduce the EOA count to two (2) by deleting `,_jsx(_components.code,{children:`Claire`}),` (keeping `,_jsx(_components.code,{children:`Alice`}),` and `,_jsx(_components.code,{children:`Bob`}),`). You can add more EOA accounts by selecting the `,_jsx(_components.code,{children:`+`}),` icon from inside the node.`]}),`\n`]}),`\n`]}),_jsxs(Note,{children:[`To leverage custom deployment scripts as part of your testing flow, see the `,_jsx(_components.a,{href:`/docs/cli/reference/#configuration-settings-for-state-space-toml`,children:`CLI reference`}),` or `,_jsx(_components.a,{href:`mail:contact@state.space`,children:`contact us`}),` for help. `]}),_jsx(Heading,{level:`3`,id:`construct-the-transaction-sequence`,children:`Construct the transaction sequence`}),_jsxs(_components.ol,{start:`3`,children:[`\n`,_jsxs(_components.li,{children:[`Drag the `,_jsx(_components.code,{children:`constructor`}),`, `,_jsx(_components.code,{children:`approve`}),`, and `,_jsx(_components.code,{children:`transferFrom`}),` function calls onto the canvas and connect them in sequence, mimicing the end-to-end user flow.`]}),`\n`]}),_jsx(_components.p,{children:`It should look like this:`}),_jsx(Frame,{caption:`transferFrom workflow`,children:_jsx(OptimizedImage,{src:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromBlank.png?fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=a8bb2d878b86202d251aeca15ca3be6c`,\"data-og-width\":`2850`,width:`2850`,\"data-og-height\":`784`,height:`784`,\"data-path\":`media/images/transferFromBlank.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromBlank.png?w=280\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=0fc1a38ac6e837ee9d39371142bc4e08 280w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromBlank.png?w=560\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=8e762ac1b38dc1ab1b2f0c58d427dd76 560w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromBlank.png?w=840\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=10b135db0811a653e03799db8055557a 840w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromBlank.png?w=1100\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=c2d323229eb653eb99ed5b389f864f1a 1100w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromBlank.png?w=1650\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=6812056de4248c9c22b2a0133a63f346 1650w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromBlank.png?w=2500\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=74a9fa1e78553f438ef33ef5e4775a11 2500w`})}),_jsx(Heading,{level:`3`,id:`configure-symbolic-parameters`,children:`Configure symbolic parameters`}),_jsx(_components.p,{children:`Transaction and function call parameters can be set to behave as symbolic values or concrete inputs.`}),_jsx(_components.p,{children:`When set to symbolic, State Space will automatically calculate the possible unique input values that will lead to an undiscovered path.`}),_jsxs(_components.p,{children:[_jsx(_components.a,{href:`/docs/overview/concepts#structured-dynamic-inputs`,children:`Learn more`}),` about how `,_jsx(_components.a,{href:`/docs/overview/concepts#structured-dynamic-inputs`,children:`structured dynamic inputs`}),` are generated.`]}),_jsxs(_components.ol,{start:`4`,children:[`\n`,_jsx(_components.li,{children:`Update the parameters so that:`}),`\n`]}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`Alice`}),` deploys the contract`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`Alice`}),` approves `,_jsx(_components.code,{children:`Bob`}),` as the `,_jsx(_components.code,{children:`_spender`})]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`Bob`}),` calls `,_jsx(_components.code,{children:`transferFrom(...)`})]}),`\n`]}),_jsx(_components.p,{children:`Leave all other fields blank to act symbolically.`}),_jsx(_components.p,{children:`It should look like this:`}),_jsx(Frame,{caption:`transferFrom workflow with symbolic and concrete parameters`,children:_jsx(OptimizedImage,{src:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFrom.png?fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=f9c2361ad02cd63cdb852b983b90e14b`,\"data-og-width\":`2848`,width:`2848`,\"data-og-height\":`766`,height:`766`,\"data-path\":`media/images/transferFrom.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFrom.png?w=280\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=9b86aa6ce86302e14834bf1945d50adc 280w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFrom.png?w=560\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=f0b3e82b8d49238dfa6bb37295e572c9 560w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFrom.png?w=840\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=f0d88fd4323e06d0107c01fc02ef8c9e 840w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFrom.png?w=1100\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=0a283acf74b77cc75c3702745b2e5fbe 1100w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFrom.png?w=1650\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=9069dc6130dcfaad2275b267f75f3009 1650w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFrom.png?w=2500\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=e576fbfc547ac2c96d255fc8ba442def 2500w`})}),_jsx(_components.hr,{})]}),_jsxs(Step,{title:`Explore the state space`,id:`explore-the-state-space`,children:[_jsxs(_components.p,{children:[`Select the `,_jsx(Icon,{icon:`play`,iconType:`solid`}),` icon to start.`]}),_jsx(_components.p,{children:`Discovered execution paths will appear dynamically during execution. Overall coverage metrics will be displayed once the state space exploration is completed.`}),_jsxs(_components.p,{children:[_jsx(_components.a,{href:`contact@state.space`,children:`Contact us`}),` if you experience any issues.`]}),_jsx(_components.p,{children:`Upon completion, you should see a total 39 test cases (execution paths) with a 37% health score.`}),_jsx(Info,{children:`Health scores are a function of line, branch, and functional coverage metrics.`}),_jsxs(_components.p,{children:[`For a more in-depth coverage analysis, open the coverage view in a new tab by selecting the run number under “Run History”. Select your contract on the left navigation menu and view the covered lines under the `,_jsx(_components.code,{children:`source`}),` tab.`]}),_jsx(_components.p,{children:`Feel free to experiement with different parameter settings and sequences to analyze behavior and maximize coverage.`}),_jsx(Warning,{children:`State Space explosion can easily occur with fully symbolic parameters and long sequences. We recommend constraining your analysis to your test objective to get meaningful, digestable results. If you accidentally encounter this, right click the active run (in Run History) to cancel the exploration run.`}),_jsx(_components.hr,{})]}),_jsxs(Step,{title:`Analyze and validate results`,id:`analyze-and-validate-results`,children:[_jsx(_components.p,{children:`Scroll through and locate the test case where:`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`Bob`}),` approves `,_jsx(_components.code,{children:`Aice`}),` to spend an allowance of `,_jsx(_components.code,{children:`uint MAX`}),` tokens, `,_jsx(_components.strong,{children:`AND`})]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`Alice`}),` successfully transfers 1 token from `,_jsx(_components.code,{children:`Bob's`}),` account.`]}),`\n`]}),_jsx(_components.p,{children:`It should look like this:`}),_jsx(Frame,{caption:`Successful transaction`,children:_jsx(OptimizedImage,{src:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsSuccess.png?fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=7a2c0797c6dca2150c3f7d3f213c6e9a`,\"data-og-width\":`2002`,width:`2002`,\"data-og-height\":`216`,height:`216`,\"data-path\":`media/images/transferFromResultsSuccess.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsSuccess.png?w=280\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=7c8acf0a0e2c8c1b125f5d9d2d05bb4b 280w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsSuccess.png?w=560\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=79ad21955990858658c23409eb59e2a6 560w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsSuccess.png?w=840\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=8f1a26216f620289ece0c01a6a344eac 840w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsSuccess.png?w=1100\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=bc117ad4ddb00aa3996c8dc38de48dd3 1100w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsSuccess.png?w=1650\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=444e2e153140b1f82c54c7aa1b51f00f 1650w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsSuccess.png?w=2500\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=cf414c65b314bd290e84931ec424e3da 2500w`})}),_jsx(_components.p,{children:`Transaction highlighted with green indicates a successful transaction occurred. Transactions ending with a red highlight indicate a reverting path.`}),_jsxs(_components.p,{children:[`Here is an example of a reverted execution path (expected behavior) where State Space autonomously tested a transferFrom amount greater than the `,_jsx(_components.code,{children:`umax`}),` spending allowance:`]}),_jsx(Frame,{caption:`Reverted transaction`,children:_jsx(OptimizedImage,{src:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsRevert.png?fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=5a52f379e822049647ff2332b955728c`,\"data-og-width\":`2202`,width:`2202`,\"data-og-height\":`216`,height:`216`,\"data-path\":`media/images/transferFromResultsRevert.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsRevert.png?w=280\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=fb656e3a76e2363a6ebbdecfb4cd4f3e 280w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsRevert.png?w=560\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=69d828190a7dbd82d47ba91682e7ac78 560w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsRevert.png?w=840\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=ba1cf05c0d521251fad6070242617751 840w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsRevert.png?w=1100\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=c1a66132e42dbd4108c44f4ecb9bb171 1100w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsRevert.png?w=1650\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=22955a95dd2b0099993e7f8bac25796e 1650w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromResultsRevert.png?w=2500\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=eec39d355c5feab04eed1c0e4439a476 2500w`})}),_jsx(Heading,{level:`3`,id:`analyze-the-state-behavior`,children:`Analyze the state behavior:`}),_jsxs(_components.ol,{children:[`\n`,_jsx(_components.li,{children:`From the results, click a function call in the transaction sequence to analyze contract values, state changes, balances, and emitted events.`}),`\n`,_jsxs(_components.li,{children:[`Review all state values and changes for correctness.`,`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Green highlighted text indicates new state value`}),`\n`,_jsx(_components.li,{children:`Red → Green format indicates a state change (prior → new state)`}),`\n`]}),`\n`]}),`\n`]}),_jsx(Frame,{caption:`Test case state analysis`,children:_jsx(`video`,{loop:true,autoPlay:true,controls:true,src:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/video/transferFromState.mp4?fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=5212d148d37e11b6633fbe4e067d8523`,\"data-path\":`media/video/transferFromState.mp4`,playsInline:`true`,muted:`true`})}),_jsx(Heading,{level:`3`,id:`validate-states`,children:`Validate states`}),_jsxs(_components.ol,{start:`3`,children:[`\n`,_jsxs(_components.li,{children:[`Mark states as “Valid” by clicking through the shield icon:`,`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Green shield: Valid state`}),`\n`,_jsx(_components.li,{children:`Red shield: Invalid state`}),`\n`,_jsx(_components.li,{children:`Grey shield: Not reviewed`}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`Each validation is tracked in the `,_jsx(_components.code,{children:`History`}),` tab.`]}),`\n`,_jsx(_components.li,{children:`Multiple reviewers with can collaborate validating state values.`}),`\n`]}),_jsx(`video`,{loop:true,autoPlay:true,src:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/video/transferFromValidate.mp4?fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=6e7a0c00e9fc31fd3c230d91cf754c82`,\"data-path\":`media/video/transferFromValidate.mp4`,playsInline:`true`,muted:`true`}),_jsx(_components.hr,{})]}),_jsxs(Step,{title:`Exporting to Hardhat or Foundry`,id:`exporting-to-hardhat-or-foundry`,children:[_jsx(_components.p,{children:`Select the “Export Test” button, and choose your framework (Hardhat or Foundry) to copy or download the test.`}),_jsx(_components.p,{children:`Check the “Events” box to include assertions for event emissions.`}),_jsx(Frame,{caption:`Export Test Case to Foundry or Hardhat`,children:_jsx(OptimizedImage,{src:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromExportTestCase.png?fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=3b82ba52494e9893bc56c77dd1b228eb`,\"data-og-width\":`3816`,width:`3816`,\"data-og-height\":`2092`,height:`2092`,\"data-path\":`media/images/transferFromExportTestCase.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromExportTestCase.png?w=280\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=b5e2da7e51b07ce7e83681060dce0b96 280w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromExportTestCase.png?w=560\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=64164ca942961c36d9c2bbbe13d5622b 560w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromExportTestCase.png?w=840\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=8446e92e9d4f6fb0a9c681ec966497b0 840w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromExportTestCase.png?w=1100\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=1470af9d7544bf124612573becdf91f9 1100w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromExportTestCase.png?w=1650\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=0c85e1b4bf56dab1c403d07e6c0cf682 1650w, https://mintcdn.com/statespace/rNXPzvVNWkIZ4wei/media/images/transferFromExportTestCase.png?w=2500\u0026fit=max\u0026auto=format\u0026n=rNXPzvVNWkIZ4wei\u0026q=85\u0026s=434973cfb603eb885f85354d1305dcd8 2500w`})})]})]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"ERC20: End-to-End Testing of transferFrom","sidebarTitle":"ERC20: End-to-End Testing of transferFrom","icon":"arrow-right-arrow-left","description":"Tutorial on end-to-end testing of transferFrom user flow","href":"/docs/tutorials/erc20-transferfrom"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Setup","slug":"setup","depth":2,"children":[{"title":"Install State Space CLI:","slug":"install-state-space-cli","depth":3,"children":[]},{"title":"Download the ERC20 example from our Github repo","slug":"download-the-erc20-example-from-our-github-repo","depth":3,"children":[]}]},{"title":"Configure and Deploy","slug":"configure-and-deploy","depth":2,"children":[{"title":"Initialize project","slug":"initialize-project","depth":3,"children":[]},{"title":"Update configuration","slug":"update-configuration","depth":3,"children":[]},{"title":"Deploy to State Space","slug":"deploy-to-state-space","depth":3,"children":[]}]},{"title":"View in Workbench","slug":"view-in-workbench","depth":2,"children":[]},{"title":"Design a test scenario","slug":"design-a-test-scenario","depth":2,"children":[{"title":"Configure a basic deployment","slug":"configure-a-basic-deployment","depth":3,"children":[]},{"title":"Construct the transaction sequence","slug":"construct-the-transaction-sequence","depth":3,"children":[]},{"title":"Configure symbolic parameters","slug":"configure-symbolic-parameters","depth":3,"children":[]}]},{"title":"Explore the state space","slug":"explore-the-state-space","depth":2,"children":[]},{"title":"Analyze and validate results","slug":"analyze-and-validate-results","depth":2,"children":[{"title":"Analyze the state behavior:","slug":"analyze-the-state-behavior","depth":3,"children":[]},{"title":"Validate states","slug":"validate-states","depth":3,"children":[]}]},{"title":"Exporting to Hardhat or Foundry","slug":"exporting-to-hardhat-or-foundry","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"statespace","siteName":"State Space","title":"ERC20: End-to-End Testing of transferFrom - State Space","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-01-07T06:47:15.090Z","originalFileLocation":"docs/tutorials/erc20-transferfrom.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"dark\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Tutorial on end-to-end testing of transferFrom user flow\">\u003cmeta data-mint-head name=\"application-name\" content=\"State Space\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"ERC20: End-to-End Testing of transferFrom - State Space\">\u003cmeta data-mint-head property=\"og:description\" content=\"Tutorial on end-to-end testing of transferFrom user flow\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"State Space\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.state.space/docs/tutorials/erc20-transferfrom\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://statespace.mintlify.app/_mintlify/api/og/v2?division=Tutorials\u0026amp;title=ERC20%3A+End-to-End+Testing+of+transferFrom\u0026amp;description=Tutorial+on+end-to-end+testing+of+transferFrom+user+flow\u0026amp;theme=c66090b754eafb6a0a71498b\u0026amp;signature=212953ea63665b74e8c8e4371ed84f5cbb6b59d6f15680decc90e722469d84ef\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"ERC20: End-to-End Testing of transferFrom - State Space\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Tutorial on end-to-end testing of transferFrom user flow\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://statespace.mintlify.app/_mintlify/api/og/v2?division=Tutorials\u0026amp;title=ERC20%3A+End-to-End+Testing+of+transferFrom\u0026amp;description=Tutorial+on+end-to-end+testing+of+transferFrom+user+flow\u0026amp;theme=c66090b754eafb6a0a71498b\u0026amp;signature=212953ea63665b74e8c8e4371ed84f5cbb6b59d6f15680decc90e722469d84ef\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/statespace/25idwkV6kfb3zWSf/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"State Space\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0D9373\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.state.space/docs/tutorials/erc20-transferfrom\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/docs/tutorials/erc20-transferfrom.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.state.space/#organization\",\"name\":\"State Space\",\"url\":\"https://docs.state.space\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintlify.s3.us-west-1.amazonaws.com/statespace/images/LogoLightMode.svg\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.state.space#website\",\"name\":\"State Space\",\"url\":\"https://docs.state.space\",\"publisher\":{\"@id\":\"https://docs.state.space/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom#webpage\",\"url\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom\",\"name\":\"ERC20: End-to-End Testing of transferFrom\",\"description\":\"Tutorial on end-to-end testing of transferFrom user flow\",\"dateModified\":\"2026-01-07T06:47:15.090Z\",\"isPartOf\":{\"@id\":\"https://docs.state.space#website\"},\"breadcrumb\":{\"@id\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Tutorials\",\"item\":\"https://docs.state.space/docs/tutorials/erc20-pause-mint\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"ERC20: End-to-End Testing of transferFrom\",\"item\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom#article\",\"headline\":\"ERC20: End-to-End Testing of transferFrom\",\"name\":\"ERC20: End-to-End Testing of transferFrom\",\"description\":\"Tutorial on end-to-end testing of transferFrom user flow\",\"url\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom\",\"mainEntityOfPage\":{\"@id\":\"https://docs.state.space/docs/tutorials/erc20-transferfrom#webpage\"},\"dateModified\":\"2026-01-07T06:47:15.090Z\",\"publisher\":{\"@id\":\"https://docs.state.space/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.state.space#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"State Space\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"State Space","nav":{"global":null,"tabs":[{"tab":"Documentation","groups":[{"group":"Overview","pages":[{"title":"Introduction","sidebarTitle":"Introduction","icon":"house","href":"/docs/overview/intro"},{"title":"Platform Features","sidebarTitle":"Features","icon":"layer-group","href":"/docs/overview/features"}]},{"group":"Getting Started","pages":[{"title":"Quickstart","sidebarTitle":"Quickstart","icon":"rocket","href":"/docs/overview/quickstart"},{"title":"Concepts","sidebarTitle":"Concepts","icon":"book-open","href":"/docs/overview/concepts"},{"title":"How it works","sidebarTitle":"How it works","icon":"gears","href":"/docs/overview/howitworks"}]},{"group":"Tutorials","pages":[{"title":"ERC20: Validate the Pausable Feature","sidebarTitle":"ERC20: Pause-->Mint","icon":"circle-pause","description":"Sandbox tutorial","href":"/docs/tutorials/erc20-pause-mint"},{"title":"ERC20: End-to-End Testing of transferFrom","sidebarTitle":"ERC20: End-to-End Testing of transferFrom","icon":"arrow-right-arrow-left","href":"/docs/tutorials/erc20-transferfrom"}]},{"group":"State Space CLI","pages":[{"title":"State Space CLI","sidebarTitle":"CLI Quickstart","icon":"rocket","description":null,"href":"/docs/cli/quickstart"},{"title":"CLI Reference","sidebarTitle":"Usage","icon":"code","href":"/docs/cli/reference"},{"title":"Configuration Properties","sidebarTitle":"Settings","icon":"gear","href":"/docs/cli/settings"},{"title":"Hardhat Plugin","sidebarTitle":"Hardhat Plugin","icon":"helmet-safety","href":"/docs/cli/hardhatplugin"}]}]},{"tab":"Changelog","pages":[{"title":"Changelog","href":"/docs/changelog/updates"}]}]},"basePath":"","title":"ERC20: End-to-End Testing of transferFrom - State Space","buildId":"dpl_36EDyZCDfRuoUD9af5CH4M1bqY2h","deploymentId":"dpl_36EDyZCDfRuoUD9af5CH4M1bqY2h","dynamics":["07c022d89b","a74be98ba7"],"chunks":["Frame-DW6UFrUy.js","DynamicMarkdown-B62XqCSI.js","zoom-image-lightbox-eHyQ0tlO.js","lightbox-handle-r17bVUz3.js"]}