Skip to content

Commit

Permalink
PKG -- [sdk] Add support for new import format
Browse files Browse the repository at this point in the history
  • Loading branch information
justinbarry committed Feb 9, 2023
1 parent 7d193ab commit 48ff433
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 65 deletions.
5 changes: 5 additions & 0 deletions .changeset/shiny-bobcats-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@onflow/config": minor
---

Add support for `import "ContractName"` syntax in scripts and transactions.
63 changes: 54 additions & 9 deletions packages/sdk/src/resolve/resolve-cadence.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
import {isTransaction, isScript, get} from "../interaction/interaction.js"
import {invariant} from "@onflow/util-invariant"
import {config} from "@onflow/config"
import * as logger from "@onflow/util-logger"

const isFn = v => typeof v === "function"
const isString = v => typeof v === "string"

const oldIdentifierPatternFn = () => /\b(0x\w+)\b/g
function isOldIdentifierSyntax(cadence) {
return oldIdentifierPatternFn().test(cadence)
}

const newIdentifierPatternFn = () => /import\s+"(\w+)"/g
function isNewIdentifierSyntax(cadence) {
return newIdentifierPatternFn().test(cadence)
}

function getContractIdentifierSyntaxMatches(cadence) {
return cadence.matchAll(newIdentifierPatternFn())
}

export async function resolveCadence(ix) {
if (isTransaction(ix) || isScript(ix)) {
var cadence = get(ix, "ix.cadence")
invariant(
isFn(cadence) || isString(cadence),
"Cadence needs to be a function or a string."
)
if (isFn(cadence)) cadence = await cadence({})
invariant(isString(cadence), "Cadence needs to be a string at this point.")
ix.message.cadence = await config()
if (!isTransaction(ix) && !isScript(ix)) return ix

var cadence = get(ix, "ix.cadence")

invariant(
isFn(cadence) || isString(cadence),
"Cadence needs to be a function or a string."
)
if (isFn(cadence)) cadence = await cadence({})
invariant(isString(cadence), "Cadence needs to be a string at this point.")
invariant(
!isOldIdentifierSyntax(cadence) || !isNewIdentifierSyntax(cadence),
"Both account identifier and contract identifier syntax not simultaneously supported."
)
if (isOldIdentifierSyntax(cadence)) {
cadence = await config()
.where(/^0x/)
.then(d =>
Object.entries(d).reduce((cadence, [key, value]) => {
Expand All @@ -24,5 +46,28 @@ export async function resolveCadence(ix) {
)
}

if (isNewIdentifierSyntax(cadence)) {
for (const [fullMatch, contractName] of getContractIdentifierSyntaxMatches(
cadence
)) {
const address = await config().get(`system.contracts.${contractName}`)
if (address) {
cadence = cadence.replace(
fullMatch,
`import ${contractName} from ${address}`
)
} else {
logger.log({
title: "Contract Placeholder not found",
message: `Cannot find a value for contract placeholder ${contractName}. Please add to your flow.json or explicitly add it to the config 'contracts.*' namespace.`,
level: logger.LEVELS.warn,
})
}
}
}

// We need to move this over in any case.
ix.message.cadence = cadence

return ix
}
196 changes: 140 additions & 56 deletions packages/sdk/src/resolve/resolve-cadence.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,71 @@ import {interaction, pipe, put, makeScript} from "../interaction/interaction.js"
import {resolveCadence} from "./resolve-cadence.js"
import {config} from "@onflow/config"

const log = msg => ix => (console.log(msg, ix), ix)
const idle = () => new Promise(resolve => setTimeout(resolve), 0)

describe("resolveCadence", () => {
test("cadence is a string", async () => {
const CADENCE = "CADENCE_STRING"

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toBe(CADENCE)
})

test("cadence is a function", async () => {
const CADENCE = async function () {
return "CADENCE_ASYNC_FUNCTION"
}

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toBe(await CADENCE())
})

test("replaces all addresses from config", async () => {
const CADENCE = async function () {
return `
describe("0xHelloWorld-style account identifier syntax", () => {
test("cadence is a string", async () => {
const CADENCE = "CADENCE_STRING"

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toBe(CADENCE)
})

test("cadence is a function", async () => {
const CADENCE = async function () {
return "CADENCE_ASYNC_FUNCTION"
}

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toBe(await CADENCE())
})

test("replaces all addresses from config", async () => {
const CADENCE = async function () {
return `
import MyContract from 0xMY_CONTRACT_ADDRESS
pub fun main(): Address {
return 0xMY_CONTRACT_ADDRESS
}
`
}
}

const RESULT = async function () {
return `
const RESULT = async function () {
return `
import MyContract from 0x123abc
pub fun main(): Address {
return 0x123abc
}
`
}
}

config.put("0xMY_CONTRACT_ADDRESS", "0x123abc")
await config.put("0xMY_CONTRACT_ADDRESS", "0x123abc")

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())
const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toEqual(await RESULT())
})
expect(ix.message.cadence).toEqual(await RESULT())
})

test("similar config names do not replace each other", async () => {
const CADENCE = async function () {
return `
test("similar config names do not replace each other", async () => {
const CADENCE = async function () {
return `
import FooBar from 0xFoo
import FooBar from 0xFooBar
Expand All @@ -82,10 +83,10 @@ describe("resolveCadence", () => {
pub fun otherTwo(): Address {return 0xFoo}
pub fun otherThree(): Address {return 0xFooBar}
`
}
}

const RESULT = async function () {
return `
const RESULT = async function () {
return `
import FooBar from 0x123
import FooBar from 0x456
Expand All @@ -102,16 +103,99 @@ describe("resolveCadence", () => {
pub fun otherTwo(): Address {return 0x123}
pub fun otherThree(): Address {return 0x456}
`
}
}

config.put("0xFoo", "0x123").put("0xFooBar", "0x456")

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toEqual(await RESULT())
})
})

describe("new Identifier syntax", () => {
test("single import statement", async () => {
const CADENCE = `import "Foo"
pub fun main(): Address {
return "Foo"
}`

const expected = `import Foo from 0x1
pub fun main(): Address {
return "Foo"
}`

await config().put("system.contracts.Foo", "0x1")
await idle()

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toEqual(expected)
})

test("multiple import statements with only one defined", async () => {
const CADENCE = `import "Foo"
import "Bar"
pub fun main(): Address {
return "Foo"
}`

const expected = `import Foo from 0x1
import "Bar"
pub fun main(): Address {
return "Foo"
}`

await config().put("system.contracts.Foo", "0x1")
await idle()

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toEqual(expected)
})

test("multiple import statements", async () => {
const CADENCE = `import "Foo"
import "Bar"
pub fun main(): Address {
return "Foo"
}`

const expected = `import Foo from 0x1
import Bar from 0x2
pub fun main(): Address {
return "Foo"
}`

config.put("0xFoo", "0x123").put("0xFooBar", "0x456")
await config().put("system.contracts.Foo", "0x1")
await config().put("system.contracts.Bar", "0x2")
await idle()

const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())
const ix = await pipe([
makeScript,
put("ix.cadence", CADENCE),
resolveCadence,
])(interaction())

expect(ix.message.cadence).toEqual(await RESULT())
expect(ix.message.cadence).toEqual(expected)
})
})
})

0 comments on commit 48ff433

Please sign in to comment.