@verto/component
Advanced tools
Comparing version 0.0.4 to 0.0.5
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
@@ -20,2 +21,8 @@ var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
var __esm = (fn, res) => function __init() { | ||
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; | ||
}; | ||
var __commonJS = (cb, mod) => function __require() { | ||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; | ||
}; | ||
var __async = (__this, __arguments, generator) => { | ||
@@ -43,45 +50,2 @@ return new Promise((resolve, reject) => { | ||
// src/utils.ts | ||
var feeWallet = "SMft-XozLyxl0ztM-gPSYKvlZVCBiiftNIb4kGFI7wg"; | ||
var ensureValidTransfer = (tokenID, transferTx, caller) => __async(void 0, null, function* () { | ||
yield ensureValidInteraction(tokenID, transferTx); | ||
try { | ||
const tx = yield SmartWeave.unsafeClient.transactions.get(transferTx); | ||
tx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
const input = JSON.parse(tag.get("value", { decode: true, string: true })); | ||
ContractAssert( | ||
input.function === "transfer", | ||
"The interaction is not a transfer" | ||
); | ||
ContractAssert( | ||
input.target === getContractID(), | ||
"The target of this transfer is not this contract" | ||
); | ||
ContractAssert(input.qty && input.qty > 0, "Invalid transfer quantity"); | ||
} | ||
}); | ||
const transferOwner = tx.get("owner"); | ||
const transferOwnerAddress = yield SmartWeave.unsafeClient.wallets.ownerToAddress(transferOwner); | ||
ContractAssert( | ||
transferOwnerAddress === caller, | ||
"Transfer owner is not the order creator" | ||
); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
}); | ||
var ensureValidInteraction = (contractID, interactionID) => __async(void 0, null, function* () { | ||
const { | ||
validity: contractTxValidities | ||
} = yield SmartWeave.contracts.readContractState(contractID, void 0, true); | ||
ContractAssert( | ||
interactionID in contractTxValidities, | ||
"The interaction is not associated with this contract" | ||
); | ||
ContractAssert( | ||
contractTxValidities[interactionID], | ||
"The interaction was invalid" | ||
); | ||
}); | ||
var isAddress = (addr) => /[a-z0-9_-]{43}/i.test(addr); | ||
function tagPatch(tags) { | ||
@@ -104,221 +68,147 @@ if (Array.isArray(tags)) | ||
} | ||
var feeWallet, ensureValidTransfer, ensureValidInteraction, isAddress; | ||
var init_utils = __esm({ | ||
"src/utils.ts"() { | ||
feeWallet = "SMft-XozLyxl0ztM-gPSYKvlZVCBiiftNIb4kGFI7wg"; | ||
ensureValidTransfer = (tokenID, transferTx, caller) => __async(void 0, null, function* () { | ||
yield ensureValidInteraction(tokenID, transferTx); | ||
try { | ||
const tx = yield SmartWeave.unsafeClient.transactions.get(transferTx); | ||
tx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
const input = JSON.parse(tag.get("value", { decode: true, string: true })); | ||
ContractAssert( | ||
input.function === "transfer", | ||
"The interaction is not a transfer" | ||
); | ||
ContractAssert( | ||
input.target === getContractID(), | ||
"The target of this transfer is not this contract" | ||
); | ||
ContractAssert(input.qty && input.qty > 0, "Invalid transfer quantity"); | ||
} | ||
}); | ||
const transferOwner = tx.get("owner"); | ||
const transferOwnerAddress = yield SmartWeave.unsafeClient.wallets.ownerToAddress(transferOwner); | ||
ContractAssert( | ||
transferOwnerAddress === caller, | ||
"Transfer owner is not the order creator" | ||
); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
}); | ||
ensureValidInteraction = (contractID, interactionID) => __async(void 0, null, function* () { | ||
const { | ||
validity: contractTxValidities | ||
} = yield SmartWeave.contracts.readContractState(contractID, void 0, true); | ||
ContractAssert( | ||
interactionID in contractTxValidities, | ||
"The interaction is not associated with this contract" | ||
); | ||
ContractAssert( | ||
contractTxValidities[interactionID], | ||
"The interaction was invalid" | ||
); | ||
}); | ||
isAddress = (addr) => /[a-z0-9_-]{43}/i.test(addr); | ||
} | ||
}); | ||
// src/modules/addPair.ts | ||
var AddPair = (state, action) => __async(void 0, null, function* () { | ||
var _a, _b; | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const newPair = input.pair; | ||
ContractAssert(newPair !== getContractID(), "Cannot add self as a pair"); | ||
ContractAssert( | ||
!pairs.find(({ pair: existingPair }) => existingPair.includes(newPair)), | ||
"This pair already exists" | ||
); | ||
ContractAssert(/[a-z0-9_-]{43}/i.test(newPair), "Pair contract is invalid"); | ||
try { | ||
const tokenState = yield SmartWeave.contracts.readContractState(newPair); | ||
ContractAssert( | ||
(tokenState == null ? void 0 : tokenState.ticker) && (tokenState == null ? void 0 : tokenState.balances), | ||
"Contract is not a valid token" | ||
); | ||
ContractAssert( | ||
typeof tokenState.ticker === "string", | ||
"Contract ticker is not a string" | ||
); | ||
for (const addr in tokenState.balances) { | ||
var AddPair; | ||
var init_addPair = __esm({ | ||
"src/modules/addPair.ts"() { | ||
init_utils(); | ||
AddPair = (state, action) => __async(void 0, null, function* () { | ||
var _a, _b; | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const newPair = input.pair; | ||
ContractAssert(newPair !== getContractID(), "Cannot add self as a pair"); | ||
ContractAssert( | ||
typeof tokenState.balances[addr] === "number", | ||
`Invalid balance for "${addr}" in contract "${newPair}"` | ||
!pairs.find(({ pair: existingPair }) => existingPair.includes(newPair)), | ||
"This pair already exists" | ||
); | ||
} | ||
const tradeableSetting = (_b = (_a = tokenState == null ? void 0 : tokenState.settings) == null ? void 0 : _a.find( | ||
([settingName]) => settingName === "isTradeable" | ||
)) == null ? void 0 : _b[1]; | ||
ContractAssert( | ||
tradeableSetting === true || tradeableSetting === void 0, | ||
`This token does not allow trading (${newPair})` | ||
); | ||
ContractAssert( | ||
!!tokenState.invocations, | ||
'Contract does not have an "invocations" filed, making it incompatible with FCP' | ||
); | ||
ContractAssert( | ||
!!tokenState.foreignCalls, | ||
'Contract does not have an "foreignCalls" filed, making it incompatible with FCP' | ||
); | ||
} catch (e) { | ||
throw new ContractError(e); | ||
ContractAssert(/[a-z0-9_-]{43}/i.test(newPair), "Pair contract is invalid"); | ||
try { | ||
const tokenState = yield SmartWeave.contracts.readContractState(newPair); | ||
ContractAssert( | ||
(tokenState == null ? void 0 : tokenState.ticker) && (tokenState == null ? void 0 : tokenState.balances), | ||
"Contract is not a valid token" | ||
); | ||
ContractAssert( | ||
typeof tokenState.ticker === "string", | ||
"Contract ticker is not a string" | ||
); | ||
for (const addr in tokenState.balances) { | ||
ContractAssert( | ||
typeof tokenState.balances[addr] === "number", | ||
`Invalid balance for "${addr}" in contract "${newPair}"` | ||
); | ||
} | ||
const tradeableSetting = (_b = (_a = tokenState == null ? void 0 : tokenState.settings) == null ? void 0 : _a.find( | ||
([settingName]) => settingName === "isTradeable" | ||
)) == null ? void 0 : _b[1]; | ||
ContractAssert( | ||
tradeableSetting === true || tradeableSetting === void 0, | ||
`This token does not allow trading (${newPair})` | ||
); | ||
ContractAssert( | ||
!!tokenState.invocations, | ||
'Contract does not have an "invocations" filed, making it incompatible with FCP' | ||
); | ||
ContractAssert( | ||
!!tokenState.foreignCalls, | ||
'Contract does not have an "foreignCalls" filed, making it incompatible with FCP' | ||
); | ||
} catch (e) { | ||
throw new ContractError(e); | ||
} | ||
state.pairs.push({ | ||
pair: [getContractID(), newPair], | ||
orders: [] | ||
}); | ||
return state; | ||
}); | ||
} | ||
state.pairs.push({ | ||
pair: [getContractID(), newPair], | ||
orders: [] | ||
}); | ||
return state; | ||
}); | ||
// src/modules/cancelOrder.ts | ||
var CancelOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const orderTxID = input.orderID; | ||
ContractAssert(isAddress(orderTxID), "Invalid order ID"); | ||
const allOrders = state.pairs.map((pair) => pair.orders).flat(1); | ||
const order = allOrders.find(({ id }) => id === orderTxID); | ||
ContractAssert(order !== void 0, "Order does not exist"); | ||
ContractAssert( | ||
order.creator === caller, | ||
"Caller is not the creator of the order" | ||
); | ||
state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: order.token, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: order.quantity | ||
} | ||
}); | ||
const acitvePair = state.pairs.find( | ||
(pair) => pair.orders.find(({ id }) => id === orderTxID) | ||
); | ||
acitvePair.orders = acitvePair.orders.filter(({ id }) => id !== orderTxID); | ||
return state; | ||
var CancelOrder; | ||
var init_cancelOrder = __esm({ | ||
"src/modules/cancelOrder.ts"() { | ||
init_utils(); | ||
CancelOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const orderTxID = input.orderID; | ||
ContractAssert(isAddress(orderTxID), "Invalid order ID"); | ||
const allOrders = state.pairs.map((pair) => pair.orders).flat(1); | ||
const order = allOrders.find(({ id }) => id === orderTxID); | ||
ContractAssert(order !== void 0, "Order does not exist"); | ||
ContractAssert( | ||
order.creator === caller, | ||
"Caller is not the creator of the order" | ||
); | ||
state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: order.token, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: order.quantity | ||
} | ||
}); | ||
const acitvePair = state.pairs.find( | ||
(pair) => pair.orders.find(({ id }) => id === orderTxID) | ||
); | ||
acitvePair.orders = acitvePair.orders.filter(({ id }) => id !== orderTxID); | ||
return state; | ||
}); | ||
} | ||
}); | ||
// src/modules/createOrder.ts | ||
var CreateOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const usedPair = input.pair; | ||
const tokenTx = input.transaction; | ||
const price = input.price; | ||
ContractAssert( | ||
isAddress(usedPair[0]) && isAddress(usedPair[1]), | ||
"One of two supplied pairs is invalid" | ||
); | ||
ContractAssert( | ||
price === void 0 || price === null || price > 0, | ||
"Price must be greater than 0" | ||
); | ||
let contractID = ""; | ||
let contractInput; | ||
let transferTx; | ||
try { | ||
transferTx = yield SmartWeave.unsafeClient.transactions.get(tokenTx); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
transferTx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Contract") { | ||
contractID = tag.get("value", { decode: true, string: true }); | ||
} | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
contractInput = JSON.parse( | ||
tag.get("value", { decode: true, string: true }) | ||
); | ||
} | ||
}); | ||
ContractAssert( | ||
typeof contractID === "string", | ||
"Invalid contract ID in transfer: not a string" | ||
); | ||
ContractAssert( | ||
contractID !== "", | ||
"No contract ID found in the transfer transaction" | ||
); | ||
ContractAssert( | ||
!state.usedTransfers.includes(tokenTx), | ||
"This transfer has already been used for an order" | ||
); | ||
ContractAssert(isAddress(contractID), "Invalid contract ID format"); | ||
yield ensureValidTransfer(contractID, tokenTx, caller); | ||
const refundTransfer = () => state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: contractID, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: contractInput.qty | ||
} | ||
}); | ||
const fromToken = usedPair[0]; | ||
if (fromToken !== contractID) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "Invalid transfer transaction, using the wrong token. The transferred token has to be the first item in the pair" | ||
} | ||
}; | ||
} | ||
const pairIndex = pairs.findIndex( | ||
({ pair }) => pair.includes(usedPair[0]) && pair.includes(usedPair[1]) | ||
); | ||
if (pairIndex === -1) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "This pair does not exist yet" | ||
} | ||
}; | ||
} | ||
const sortedOrderbook = state.pairs[pairIndex].orders.sort( | ||
(a, b) => a.price > b.price ? 1 : -1 | ||
); | ||
const dominantToken = state.pairs[pairIndex].pair[0]; | ||
try { | ||
const { orderbook, foreignCalls, matches } = matchOrder( | ||
{ | ||
pair: { | ||
dominant: dominantToken, | ||
from: contractID, | ||
to: usedPair.find((val) => val !== contractID) | ||
}, | ||
quantity: contractInput.qty, | ||
creator: caller, | ||
transaction: SmartWeave.transaction.id, | ||
transfer: tokenTx, | ||
price | ||
}, | ||
sortedOrderbook | ||
); | ||
state.pairs[pairIndex].orders = orderbook; | ||
if (matches.length > 0) { | ||
const vwap = matches.map(({ qty: volume, price: price2 }) => volume * price2).reduce((a, b) => a + b, 0) / matches.map(({ qty: volume }) => volume).reduce((a, b) => a + b, 0); | ||
state.pairs[pairIndex].priceData = { | ||
dominantToken, | ||
block: SmartWeave.block.height, | ||
vwap, | ||
matchLogs: matches | ||
}; | ||
} else { | ||
state.pairs[pairIndex].priceData = void 0; | ||
} | ||
for (let i = 0; i < foreignCalls.length; i++) { | ||
state.foreignCalls.push(foreignCalls[i]); | ||
} | ||
state.usedTransfers.push(tokenTx); | ||
return { | ||
state, | ||
result: { | ||
status: "success", | ||
message: "Order created successfully" | ||
} | ||
}; | ||
} catch (e) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: e.message | ||
} | ||
}; | ||
} | ||
}); | ||
function matchOrder(input, orderbook) { | ||
@@ -476,40 +366,198 @@ var _a, _b; | ||
} | ||
var CreateOrder; | ||
var init_createOrder = __esm({ | ||
"src/modules/createOrder.ts"() { | ||
init_utils(); | ||
CreateOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const usedPair = input.pair; | ||
const tokenTx = input.transaction; | ||
const price = input.price; | ||
ContractAssert( | ||
isAddress(usedPair[0]) && isAddress(usedPair[1]), | ||
"One of two supplied pairs is invalid" | ||
); | ||
ContractAssert( | ||
price === void 0 || price === null || price > 0, | ||
"Price must be greater than 0" | ||
); | ||
let contractID = ""; | ||
let contractInput; | ||
let transferTx; | ||
try { | ||
transferTx = yield SmartWeave.unsafeClient.transactions.get(tokenTx); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
transferTx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Contract") { | ||
contractID = tag.get("value", { decode: true, string: true }); | ||
} | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
contractInput = JSON.parse( | ||
tag.get("value", { decode: true, string: true }) | ||
); | ||
} | ||
}); | ||
ContractAssert( | ||
typeof contractID === "string", | ||
"Invalid contract ID in transfer: not a string" | ||
); | ||
ContractAssert( | ||
contractID !== "", | ||
"No contract ID found in the transfer transaction" | ||
); | ||
ContractAssert( | ||
!state.usedTransfers.includes(tokenTx), | ||
"This transfer has already been used for an order" | ||
); | ||
ContractAssert(isAddress(contractID), "Invalid contract ID format"); | ||
yield ensureValidTransfer(contractID, tokenTx, caller); | ||
const refundTransfer = () => state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: contractID, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: contractInput.qty | ||
} | ||
}); | ||
const fromToken = usedPair[0]; | ||
if (fromToken !== contractID) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "Invalid transfer transaction, using the wrong token. The transferred token has to be the first item in the pair" | ||
} | ||
}; | ||
} | ||
const pairIndex = pairs.findIndex( | ||
({ pair }) => pair.includes(usedPair[0]) && pair.includes(usedPair[1]) | ||
); | ||
if (pairIndex === -1) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "This pair does not exist yet" | ||
} | ||
}; | ||
} | ||
const sortedOrderbook = state.pairs[pairIndex].orders.sort( | ||
(a, b) => a.price > b.price ? 1 : -1 | ||
); | ||
const dominantToken = state.pairs[pairIndex].pair[0]; | ||
try { | ||
const { orderbook, foreignCalls, matches } = matchOrder( | ||
{ | ||
pair: { | ||
dominant: dominantToken, | ||
from: contractID, | ||
to: usedPair.find((val) => val !== contractID) | ||
}, | ||
quantity: contractInput.qty, | ||
creator: caller, | ||
transaction: SmartWeave.transaction.id, | ||
transfer: tokenTx, | ||
price | ||
}, | ||
sortedOrderbook | ||
); | ||
state.pairs[pairIndex].orders = orderbook; | ||
if (matches.length > 0) { | ||
const vwap = matches.map(({ qty: volume, price: price2 }) => volume * price2).reduce((a, b) => a + b, 0) / matches.map(({ qty: volume }) => volume).reduce((a, b) => a + b, 0); | ||
state.pairs[pairIndex].priceData = { | ||
dominantToken, | ||
block: SmartWeave.block.height, | ||
vwap, | ||
matchLogs: matches | ||
}; | ||
} else { | ||
state.pairs[pairIndex].priceData = void 0; | ||
} | ||
for (let i = 0; i < foreignCalls.length; i++) { | ||
state.foreignCalls.push(foreignCalls[i]); | ||
} | ||
state.usedTransfers.push(tokenTx); | ||
return { | ||
state, | ||
result: { | ||
status: "success", | ||
message: "Order created successfully" | ||
} | ||
}; | ||
} catch (e) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: e.message | ||
} | ||
}; | ||
} | ||
}); | ||
} | ||
}); | ||
// src/modules/halt.ts | ||
var Halt = (state, action) => { | ||
const caller = action.caller; | ||
ContractAssert( | ||
caller === state.emergencyHaltWallet, | ||
"Caller cannot halt or resume the protocol" | ||
); | ||
return __spreadProps(__spreadValues({}, state), { halted: !state.halted }); | ||
}; | ||
var Halt; | ||
var init_halt = __esm({ | ||
"src/modules/halt.ts"() { | ||
Halt = (state, action) => { | ||
const caller = action.caller; | ||
ContractAssert( | ||
caller === state.emergencyHaltWallet, | ||
"Caller cannot halt or resume the protocol" | ||
); | ||
return __spreadProps(__spreadValues({}, state), { halted: !state.halted }); | ||
}; | ||
} | ||
}); | ||
// src/modules/readOutbox.ts | ||
var ReadOutbox = (state, action, handle) => __async(void 0, null, function* () { | ||
const input = action.input; | ||
ContractAssert(!!input.contract, "Missing contract to invoke"); | ||
ContractAssert(input.contract !== getContractID(), "Cannot read own outbox"); | ||
const foreignState = yield SmartWeave.contracts.readContractState( | ||
input.contract | ||
); | ||
ContractAssert( | ||
!!foreignState.foreignCalls, | ||
"Contract is missing support for foreign calls" | ||
); | ||
const calls = foreignState.foreignCalls.filter( | ||
(element) => element.contract === SmartWeave.contract.id && !state.invocations.includes(element.txID) | ||
); | ||
let res = state; | ||
for (const entry of calls) { | ||
res = (yield handle(res, { caller: input.contract, input: entry.input })).state; | ||
res.invocations.push(entry.txID); | ||
var ReadOutbox; | ||
var init_readOutbox = __esm({ | ||
"src/modules/readOutbox.ts"() { | ||
init_utils(); | ||
ReadOutbox = (state, action, handle) => __async(void 0, null, function* () { | ||
const input = action.input; | ||
ContractAssert(!!input.contract, "Missing contract to invoke"); | ||
ContractAssert(input.contract !== getContractID(), "Cannot read own outbox"); | ||
const foreignState = yield SmartWeave.contracts.readContractState( | ||
input.contract | ||
); | ||
ContractAssert( | ||
!!foreignState.foreignCalls, | ||
"Contract is missing support for foreign calls" | ||
); | ||
const calls = foreignState.foreignCalls.filter( | ||
(element) => element.contract === SmartWeave.contract.id && !state.invocations.includes(element.txID) | ||
); | ||
let res = state; | ||
for (const entry of calls) { | ||
res = (yield handle(res, { caller: input.contract, input: entry.input })).state; | ||
res.invocations.push(entry.txID); | ||
} | ||
return res; | ||
}); | ||
} | ||
return res; | ||
}); | ||
// src/index.ts | ||
var src_default = { AddPair, CancelOrder, CreateOrder, Halt, ReadOutbox }; | ||
export { | ||
src_default as default | ||
}; | ||
var require_src = __commonJS({ | ||
"src/index.ts"(exports, module) { | ||
init_addPair(); | ||
init_cancelOrder(); | ||
init_createOrder(); | ||
init_halt(); | ||
init_readOutbox(); | ||
module.exports = { AddPair, CancelOrder, CreateOrder, Halt, ReadOutbox }; | ||
} | ||
}); | ||
export default require_src(); |
@@ -5,2 +5,3 @@ (() => { | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
@@ -22,2 +23,8 @@ var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
var __esm = (fn, res) => function __init() { | ||
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; | ||
}; | ||
var __commonJS = (cb, mod) => function __require() { | ||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; | ||
}; | ||
var __async = (__this, __arguments, generator) => { | ||
@@ -45,45 +52,2 @@ return new Promise((resolve, reject) => { | ||
// src/utils.ts | ||
var feeWallet = "SMft-XozLyxl0ztM-gPSYKvlZVCBiiftNIb4kGFI7wg"; | ||
var ensureValidTransfer = (tokenID, transferTx, caller) => __async(void 0, null, function* () { | ||
yield ensureValidInteraction(tokenID, transferTx); | ||
try { | ||
const tx = yield SmartWeave.unsafeClient.transactions.get(transferTx); | ||
tx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
const input = JSON.parse(tag.get("value", { decode: true, string: true })); | ||
ContractAssert( | ||
input.function === "transfer", | ||
"The interaction is not a transfer" | ||
); | ||
ContractAssert( | ||
input.target === getContractID(), | ||
"The target of this transfer is not this contract" | ||
); | ||
ContractAssert(input.qty && input.qty > 0, "Invalid transfer quantity"); | ||
} | ||
}); | ||
const transferOwner = tx.get("owner"); | ||
const transferOwnerAddress = yield SmartWeave.unsafeClient.wallets.ownerToAddress(transferOwner); | ||
ContractAssert( | ||
transferOwnerAddress === caller, | ||
"Transfer owner is not the order creator" | ||
); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
}); | ||
var ensureValidInteraction = (contractID, interactionID) => __async(void 0, null, function* () { | ||
const { | ||
validity: contractTxValidities | ||
} = yield SmartWeave.contracts.readContractState(contractID, void 0, true); | ||
ContractAssert( | ||
interactionID in contractTxValidities, | ||
"The interaction is not associated with this contract" | ||
); | ||
ContractAssert( | ||
contractTxValidities[interactionID], | ||
"The interaction was invalid" | ||
); | ||
}); | ||
var isAddress = (addr) => /[a-z0-9_-]{43}/i.test(addr); | ||
function tagPatch(tags) { | ||
@@ -106,221 +70,147 @@ if (Array.isArray(tags)) | ||
} | ||
var feeWallet, ensureValidTransfer, ensureValidInteraction, isAddress; | ||
var init_utils = __esm({ | ||
"src/utils.ts"() { | ||
feeWallet = "SMft-XozLyxl0ztM-gPSYKvlZVCBiiftNIb4kGFI7wg"; | ||
ensureValidTransfer = (tokenID, transferTx, caller) => __async(void 0, null, function* () { | ||
yield ensureValidInteraction(tokenID, transferTx); | ||
try { | ||
const tx = yield SmartWeave.unsafeClient.transactions.get(transferTx); | ||
tx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
const input = JSON.parse(tag.get("value", { decode: true, string: true })); | ||
ContractAssert( | ||
input.function === "transfer", | ||
"The interaction is not a transfer" | ||
); | ||
ContractAssert( | ||
input.target === getContractID(), | ||
"The target of this transfer is not this contract" | ||
); | ||
ContractAssert(input.qty && input.qty > 0, "Invalid transfer quantity"); | ||
} | ||
}); | ||
const transferOwner = tx.get("owner"); | ||
const transferOwnerAddress = yield SmartWeave.unsafeClient.wallets.ownerToAddress(transferOwner); | ||
ContractAssert( | ||
transferOwnerAddress === caller, | ||
"Transfer owner is not the order creator" | ||
); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
}); | ||
ensureValidInteraction = (contractID, interactionID) => __async(void 0, null, function* () { | ||
const { | ||
validity: contractTxValidities | ||
} = yield SmartWeave.contracts.readContractState(contractID, void 0, true); | ||
ContractAssert( | ||
interactionID in contractTxValidities, | ||
"The interaction is not associated with this contract" | ||
); | ||
ContractAssert( | ||
contractTxValidities[interactionID], | ||
"The interaction was invalid" | ||
); | ||
}); | ||
isAddress = (addr) => /[a-z0-9_-]{43}/i.test(addr); | ||
} | ||
}); | ||
// src/modules/addPair.ts | ||
var AddPair = (state, action) => __async(void 0, null, function* () { | ||
var _a, _b; | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const newPair = input.pair; | ||
ContractAssert(newPair !== getContractID(), "Cannot add self as a pair"); | ||
ContractAssert( | ||
!pairs.find(({ pair: existingPair }) => existingPair.includes(newPair)), | ||
"This pair already exists" | ||
); | ||
ContractAssert(/[a-z0-9_-]{43}/i.test(newPair), "Pair contract is invalid"); | ||
try { | ||
const tokenState = yield SmartWeave.contracts.readContractState(newPair); | ||
ContractAssert( | ||
(tokenState == null ? void 0 : tokenState.ticker) && (tokenState == null ? void 0 : tokenState.balances), | ||
"Contract is not a valid token" | ||
); | ||
ContractAssert( | ||
typeof tokenState.ticker === "string", | ||
"Contract ticker is not a string" | ||
); | ||
for (const addr in tokenState.balances) { | ||
var AddPair; | ||
var init_addPair = __esm({ | ||
"src/modules/addPair.ts"() { | ||
init_utils(); | ||
AddPair = (state, action) => __async(void 0, null, function* () { | ||
var _a, _b; | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const newPair = input.pair; | ||
ContractAssert(newPair !== getContractID(), "Cannot add self as a pair"); | ||
ContractAssert( | ||
typeof tokenState.balances[addr] === "number", | ||
`Invalid balance for "${addr}" in contract "${newPair}"` | ||
!pairs.find(({ pair: existingPair }) => existingPair.includes(newPair)), | ||
"This pair already exists" | ||
); | ||
} | ||
const tradeableSetting = (_b = (_a = tokenState == null ? void 0 : tokenState.settings) == null ? void 0 : _a.find( | ||
([settingName]) => settingName === "isTradeable" | ||
)) == null ? void 0 : _b[1]; | ||
ContractAssert( | ||
tradeableSetting === true || tradeableSetting === void 0, | ||
`This token does not allow trading (${newPair})` | ||
); | ||
ContractAssert( | ||
!!tokenState.invocations, | ||
'Contract does not have an "invocations" filed, making it incompatible with FCP' | ||
); | ||
ContractAssert( | ||
!!tokenState.foreignCalls, | ||
'Contract does not have an "foreignCalls" filed, making it incompatible with FCP' | ||
); | ||
} catch (e) { | ||
throw new ContractError(e); | ||
ContractAssert(/[a-z0-9_-]{43}/i.test(newPair), "Pair contract is invalid"); | ||
try { | ||
const tokenState = yield SmartWeave.contracts.readContractState(newPair); | ||
ContractAssert( | ||
(tokenState == null ? void 0 : tokenState.ticker) && (tokenState == null ? void 0 : tokenState.balances), | ||
"Contract is not a valid token" | ||
); | ||
ContractAssert( | ||
typeof tokenState.ticker === "string", | ||
"Contract ticker is not a string" | ||
); | ||
for (const addr in tokenState.balances) { | ||
ContractAssert( | ||
typeof tokenState.balances[addr] === "number", | ||
`Invalid balance for "${addr}" in contract "${newPair}"` | ||
); | ||
} | ||
const tradeableSetting = (_b = (_a = tokenState == null ? void 0 : tokenState.settings) == null ? void 0 : _a.find( | ||
([settingName]) => settingName === "isTradeable" | ||
)) == null ? void 0 : _b[1]; | ||
ContractAssert( | ||
tradeableSetting === true || tradeableSetting === void 0, | ||
`This token does not allow trading (${newPair})` | ||
); | ||
ContractAssert( | ||
!!tokenState.invocations, | ||
'Contract does not have an "invocations" filed, making it incompatible with FCP' | ||
); | ||
ContractAssert( | ||
!!tokenState.foreignCalls, | ||
'Contract does not have an "foreignCalls" filed, making it incompatible with FCP' | ||
); | ||
} catch (e) { | ||
throw new ContractError(e); | ||
} | ||
state.pairs.push({ | ||
pair: [getContractID(), newPair], | ||
orders: [] | ||
}); | ||
return state; | ||
}); | ||
} | ||
state.pairs.push({ | ||
pair: [getContractID(), newPair], | ||
orders: [] | ||
}); | ||
return state; | ||
}); | ||
// src/modules/cancelOrder.ts | ||
var CancelOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const orderTxID = input.orderID; | ||
ContractAssert(isAddress(orderTxID), "Invalid order ID"); | ||
const allOrders = state.pairs.map((pair) => pair.orders).flat(1); | ||
const order = allOrders.find(({ id }) => id === orderTxID); | ||
ContractAssert(order !== void 0, "Order does not exist"); | ||
ContractAssert( | ||
order.creator === caller, | ||
"Caller is not the creator of the order" | ||
); | ||
state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: order.token, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: order.quantity | ||
} | ||
}); | ||
const acitvePair = state.pairs.find( | ||
(pair) => pair.orders.find(({ id }) => id === orderTxID) | ||
); | ||
acitvePair.orders = acitvePair.orders.filter(({ id }) => id !== orderTxID); | ||
return state; | ||
var CancelOrder; | ||
var init_cancelOrder = __esm({ | ||
"src/modules/cancelOrder.ts"() { | ||
init_utils(); | ||
CancelOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const orderTxID = input.orderID; | ||
ContractAssert(isAddress(orderTxID), "Invalid order ID"); | ||
const allOrders = state.pairs.map((pair) => pair.orders).flat(1); | ||
const order = allOrders.find(({ id }) => id === orderTxID); | ||
ContractAssert(order !== void 0, "Order does not exist"); | ||
ContractAssert( | ||
order.creator === caller, | ||
"Caller is not the creator of the order" | ||
); | ||
state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: order.token, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: order.quantity | ||
} | ||
}); | ||
const acitvePair = state.pairs.find( | ||
(pair) => pair.orders.find(({ id }) => id === orderTxID) | ||
); | ||
acitvePair.orders = acitvePair.orders.filter(({ id }) => id !== orderTxID); | ||
return state; | ||
}); | ||
} | ||
}); | ||
// src/modules/createOrder.ts | ||
var CreateOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const usedPair = input.pair; | ||
const tokenTx = input.transaction; | ||
const price = input.price; | ||
ContractAssert( | ||
isAddress(usedPair[0]) && isAddress(usedPair[1]), | ||
"One of two supplied pairs is invalid" | ||
); | ||
ContractAssert( | ||
price === void 0 || price === null || price > 0, | ||
"Price must be greater than 0" | ||
); | ||
let contractID = ""; | ||
let contractInput; | ||
let transferTx; | ||
try { | ||
transferTx = yield SmartWeave.unsafeClient.transactions.get(tokenTx); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
transferTx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Contract") { | ||
contractID = tag.get("value", { decode: true, string: true }); | ||
} | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
contractInput = JSON.parse( | ||
tag.get("value", { decode: true, string: true }) | ||
); | ||
} | ||
}); | ||
ContractAssert( | ||
typeof contractID === "string", | ||
"Invalid contract ID in transfer: not a string" | ||
); | ||
ContractAssert( | ||
contractID !== "", | ||
"No contract ID found in the transfer transaction" | ||
); | ||
ContractAssert( | ||
!state.usedTransfers.includes(tokenTx), | ||
"This transfer has already been used for an order" | ||
); | ||
ContractAssert(isAddress(contractID), "Invalid contract ID format"); | ||
yield ensureValidTransfer(contractID, tokenTx, caller); | ||
const refundTransfer = () => state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: contractID, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: contractInput.qty | ||
} | ||
}); | ||
const fromToken = usedPair[0]; | ||
if (fromToken !== contractID) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "Invalid transfer transaction, using the wrong token. The transferred token has to be the first item in the pair" | ||
} | ||
}; | ||
} | ||
const pairIndex = pairs.findIndex( | ||
({ pair }) => pair.includes(usedPair[0]) && pair.includes(usedPair[1]) | ||
); | ||
if (pairIndex === -1) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "This pair does not exist yet" | ||
} | ||
}; | ||
} | ||
const sortedOrderbook = state.pairs[pairIndex].orders.sort( | ||
(a, b) => a.price > b.price ? 1 : -1 | ||
); | ||
const dominantToken = state.pairs[pairIndex].pair[0]; | ||
try { | ||
const { orderbook, foreignCalls, matches } = matchOrder( | ||
{ | ||
pair: { | ||
dominant: dominantToken, | ||
from: contractID, | ||
to: usedPair.find((val) => val !== contractID) | ||
}, | ||
quantity: contractInput.qty, | ||
creator: caller, | ||
transaction: SmartWeave.transaction.id, | ||
transfer: tokenTx, | ||
price | ||
}, | ||
sortedOrderbook | ||
); | ||
state.pairs[pairIndex].orders = orderbook; | ||
if (matches.length > 0) { | ||
const vwap = matches.map(({ qty: volume, price: price2 }) => volume * price2).reduce((a, b) => a + b, 0) / matches.map(({ qty: volume }) => volume).reduce((a, b) => a + b, 0); | ||
state.pairs[pairIndex].priceData = { | ||
dominantToken, | ||
block: SmartWeave.block.height, | ||
vwap, | ||
matchLogs: matches | ||
}; | ||
} else { | ||
state.pairs[pairIndex].priceData = void 0; | ||
} | ||
for (let i = 0; i < foreignCalls.length; i++) { | ||
state.foreignCalls.push(foreignCalls[i]); | ||
} | ||
state.usedTransfers.push(tokenTx); | ||
return { | ||
state, | ||
result: { | ||
status: "success", | ||
message: "Order created successfully" | ||
} | ||
}; | ||
} catch (e) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: e.message | ||
} | ||
}; | ||
} | ||
}); | ||
function matchOrder(input, orderbook) { | ||
@@ -478,38 +368,199 @@ var _a, _b; | ||
} | ||
var CreateOrder; | ||
var init_createOrder = __esm({ | ||
"src/modules/createOrder.ts"() { | ||
init_utils(); | ||
CreateOrder = (state, action) => __async(void 0, null, function* () { | ||
const caller = action.caller; | ||
const input = action.input; | ||
const pairs = state.pairs; | ||
const usedPair = input.pair; | ||
const tokenTx = input.transaction; | ||
const price = input.price; | ||
ContractAssert( | ||
isAddress(usedPair[0]) && isAddress(usedPair[1]), | ||
"One of two supplied pairs is invalid" | ||
); | ||
ContractAssert( | ||
price === void 0 || price === null || price > 0, | ||
"Price must be greater than 0" | ||
); | ||
let contractID = ""; | ||
let contractInput; | ||
let transferTx; | ||
try { | ||
transferTx = yield SmartWeave.unsafeClient.transactions.get(tokenTx); | ||
} catch (err) { | ||
throw new ContractError(err); | ||
} | ||
transferTx.get("tags").forEach((tag) => { | ||
if (tag.get("name", { decode: true, string: true }) === "Contract") { | ||
contractID = tag.get("value", { decode: true, string: true }); | ||
} | ||
if (tag.get("name", { decode: true, string: true }) === "Input") { | ||
contractInput = JSON.parse( | ||
tag.get("value", { decode: true, string: true }) | ||
); | ||
} | ||
}); | ||
ContractAssert( | ||
typeof contractID === "string", | ||
"Invalid contract ID in transfer: not a string" | ||
); | ||
ContractAssert( | ||
contractID !== "", | ||
"No contract ID found in the transfer transaction" | ||
); | ||
ContractAssert( | ||
!state.usedTransfers.includes(tokenTx), | ||
"This transfer has already been used for an order" | ||
); | ||
ContractAssert(isAddress(contractID), "Invalid contract ID format"); | ||
yield ensureValidTransfer(contractID, tokenTx, caller); | ||
const refundTransfer = () => state.foreignCalls.push({ | ||
txID: SmartWeave.transaction.id, | ||
contract: contractID, | ||
input: { | ||
function: "transfer", | ||
target: caller, | ||
qty: contractInput.qty | ||
} | ||
}); | ||
const fromToken = usedPair[0]; | ||
if (fromToken !== contractID) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "Invalid transfer transaction, using the wrong token. The transferred token has to be the first item in the pair" | ||
} | ||
}; | ||
} | ||
const pairIndex = pairs.findIndex( | ||
({ pair }) => pair.includes(usedPair[0]) && pair.includes(usedPair[1]) | ||
); | ||
if (pairIndex === -1) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: "This pair does not exist yet" | ||
} | ||
}; | ||
} | ||
const sortedOrderbook = state.pairs[pairIndex].orders.sort( | ||
(a, b) => a.price > b.price ? 1 : -1 | ||
); | ||
const dominantToken = state.pairs[pairIndex].pair[0]; | ||
try { | ||
const { orderbook, foreignCalls, matches } = matchOrder( | ||
{ | ||
pair: { | ||
dominant: dominantToken, | ||
from: contractID, | ||
to: usedPair.find((val) => val !== contractID) | ||
}, | ||
quantity: contractInput.qty, | ||
creator: caller, | ||
transaction: SmartWeave.transaction.id, | ||
transfer: tokenTx, | ||
price | ||
}, | ||
sortedOrderbook | ||
); | ||
state.pairs[pairIndex].orders = orderbook; | ||
if (matches.length > 0) { | ||
const vwap = matches.map(({ qty: volume, price: price2 }) => volume * price2).reduce((a, b) => a + b, 0) / matches.map(({ qty: volume }) => volume).reduce((a, b) => a + b, 0); | ||
state.pairs[pairIndex].priceData = { | ||
dominantToken, | ||
block: SmartWeave.block.height, | ||
vwap, | ||
matchLogs: matches | ||
}; | ||
} else { | ||
state.pairs[pairIndex].priceData = void 0; | ||
} | ||
for (let i = 0; i < foreignCalls.length; i++) { | ||
state.foreignCalls.push(foreignCalls[i]); | ||
} | ||
state.usedTransfers.push(tokenTx); | ||
return { | ||
state, | ||
result: { | ||
status: "success", | ||
message: "Order created successfully" | ||
} | ||
}; | ||
} catch (e) { | ||
refundTransfer(); | ||
return { | ||
state, | ||
result: { | ||
status: "failure", | ||
message: e.message | ||
} | ||
}; | ||
} | ||
}); | ||
} | ||
}); | ||
// src/modules/halt.ts | ||
var Halt = (state, action) => { | ||
const caller = action.caller; | ||
ContractAssert( | ||
caller === state.emergencyHaltWallet, | ||
"Caller cannot halt or resume the protocol" | ||
); | ||
return __spreadProps(__spreadValues({}, state), { halted: !state.halted }); | ||
}; | ||
var Halt; | ||
var init_halt = __esm({ | ||
"src/modules/halt.ts"() { | ||
Halt = (state, action) => { | ||
const caller = action.caller; | ||
ContractAssert( | ||
caller === state.emergencyHaltWallet, | ||
"Caller cannot halt or resume the protocol" | ||
); | ||
return __spreadProps(__spreadValues({}, state), { halted: !state.halted }); | ||
}; | ||
} | ||
}); | ||
// src/modules/readOutbox.ts | ||
var ReadOutbox = (state, action, handle) => __async(void 0, null, function* () { | ||
const input = action.input; | ||
ContractAssert(!!input.contract, "Missing contract to invoke"); | ||
ContractAssert(input.contract !== getContractID(), "Cannot read own outbox"); | ||
const foreignState = yield SmartWeave.contracts.readContractState( | ||
input.contract | ||
); | ||
ContractAssert( | ||
!!foreignState.foreignCalls, | ||
"Contract is missing support for foreign calls" | ||
); | ||
const calls = foreignState.foreignCalls.filter( | ||
(element) => element.contract === SmartWeave.contract.id && !state.invocations.includes(element.txID) | ||
); | ||
let res = state; | ||
for (const entry of calls) { | ||
res = (yield handle(res, { caller: input.contract, input: entry.input })).state; | ||
res.invocations.push(entry.txID); | ||
var ReadOutbox; | ||
var init_readOutbox = __esm({ | ||
"src/modules/readOutbox.ts"() { | ||
init_utils(); | ||
ReadOutbox = (state, action, handle) => __async(void 0, null, function* () { | ||
const input = action.input; | ||
ContractAssert(!!input.contract, "Missing contract to invoke"); | ||
ContractAssert(input.contract !== getContractID(), "Cannot read own outbox"); | ||
const foreignState = yield SmartWeave.contracts.readContractState( | ||
input.contract | ||
); | ||
ContractAssert( | ||
!!foreignState.foreignCalls, | ||
"Contract is missing support for foreign calls" | ||
); | ||
const calls = foreignState.foreignCalls.filter( | ||
(element) => element.contract === SmartWeave.contract.id && !state.invocations.includes(element.txID) | ||
); | ||
let res = state; | ||
for (const entry of calls) { | ||
res = (yield handle(res, { caller: input.contract, input: entry.input })).state; | ||
res.invocations.push(entry.txID); | ||
} | ||
return res; | ||
}); | ||
} | ||
return res; | ||
}); | ||
// src/index.ts | ||
var src_default = { AddPair, CancelOrder, CreateOrder, Halt, ReadOutbox }; | ||
var require_src = __commonJS({ | ||
"src/index.ts"(exports, module) { | ||
init_addPair(); | ||
init_cancelOrder(); | ||
init_createOrder(); | ||
init_halt(); | ||
init_readOutbox(); | ||
module.exports = { AddPair, CancelOrder, CreateOrder, Halt, ReadOutbox }; | ||
} | ||
}); | ||
require_src(); | ||
})(); |
{ | ||
"name": "@verto/component", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"author": "Tate Berenbaum<tate@verto.exchange>", | ||
@@ -5,0 +5,0 @@ "files": [ |
46607
1289