feat: collateral per slot (#390)
Co-authored-by: Eric Mastro <github@egonat.me>
This commit is contained in:
parent
86a3f74448
commit
131d003a0c
|
@ -32,33 +32,6 @@ let client = provider.getSigner(accounts[0])
|
|||
let host = provider.getSigner(accounts[1])
|
||||
```
|
||||
|
||||
Collateral
|
||||
----------
|
||||
|
||||
Hosts need to put up collateral before participating in storage contracts.
|
||||
|
||||
A host can learn about the amount of collateral that is required:
|
||||
```nim
|
||||
let config = await marketplace.config()
|
||||
let collateral = config.collateral.initialAmount
|
||||
```
|
||||
|
||||
After preparing the payment, the host can deposit collateral:
|
||||
```nim
|
||||
await storage
|
||||
.connect(host)
|
||||
.deposit(collateral)
|
||||
```
|
||||
|
||||
When a host is not participating in storage offers or contracts, it can withdraw
|
||||
its collateral:
|
||||
|
||||
```
|
||||
await storage
|
||||
.connect(host)
|
||||
.withdraw()
|
||||
```
|
||||
|
||||
Storage requests
|
||||
----------------
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@ type
|
|||
collateral*: CollateralConfig
|
||||
proofs*: ProofConfig
|
||||
CollateralConfig* = object
|
||||
initialAmount*: UInt256 # amount of collateral necessary to fill a slot
|
||||
minimumAmount*: UInt256 # frees slot when collateral drops below this minimum
|
||||
slashCriterion*: UInt256 # amount of proofs missed that lead to slashing
|
||||
slashPercentage*: UInt256 # percentage of the collateral that is slashed
|
||||
repairRewardPercentage*: uint8 # percentage of remaining collateral slot has after it has been freed
|
||||
maxNumberOfSlashes*: uint8 # frees slot when the number of slashes reaches this value
|
||||
slashCriterion*: uint16 # amount of proofs missed that lead to slashing
|
||||
slashPercentage*: uint8 # percentage of the collateral that is slashed
|
||||
ProofConfig* = object
|
||||
period*: UInt256 # proofs requirements are calculated per period (in seconds)
|
||||
timeout*: UInt256 # mark proofs as missing before the timeout (in seconds)
|
||||
|
@ -28,8 +28,8 @@ func fromTuple(_: type ProofConfig, tupl: tuple): ProofConfig =
|
|||
|
||||
func fromTuple(_: type CollateralConfig, tupl: tuple): CollateralConfig =
|
||||
CollateralConfig(
|
||||
initialAmount: tupl[0],
|
||||
minimumAmount: tupl[1],
|
||||
repairRewardPercentage: tupl[0],
|
||||
maxNumberOfSlashes: tupl[1],
|
||||
slashCriterion: tupl[2],
|
||||
slashPercentage: tupl[3]
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import std/strutils
|
||||
import pkg/chronicles
|
||||
import pkg/ethers
|
||||
import pkg/ethers/testing
|
||||
import pkg/upraises
|
||||
|
@ -8,6 +9,9 @@ import ./marketplace
|
|||
|
||||
export market
|
||||
|
||||
logScope:
|
||||
topics = "onchain market"
|
||||
|
||||
type
|
||||
OnChainMarket* = ref object of Market
|
||||
contract: Marketplace
|
||||
|
@ -25,7 +29,8 @@ func new*(_: type OnChainMarket, contract: Marketplace): OnChainMarket =
|
|||
signer: signer,
|
||||
)
|
||||
|
||||
method approveFunds*(market: OnChainMarket, amount: UInt256) {.async.} =
|
||||
proc approveFunds(market: OnChainMarket, amount: UInt256) {.async.} =
|
||||
notice "approving tokens", amount
|
||||
let tokenAddress = await market.contract.token()
|
||||
let token = Erc20Token.new(tokenAddress, market.signer)
|
||||
|
||||
|
@ -41,6 +46,7 @@ method mySlots*(market: OnChainMarket): Future[seq[SlotId]] {.async.} =
|
|||
return await market.contract.mySlots()
|
||||
|
||||
method requestStorage(market: OnChainMarket, request: StorageRequest){.async.} =
|
||||
await market.approveFunds(request.price())
|
||||
await market.contract.requestStorage(request)
|
||||
|
||||
method getRequest(market: OnChainMarket,
|
||||
|
@ -93,7 +99,9 @@ method getActiveSlot*(
|
|||
method fillSlot(market: OnChainMarket,
|
||||
requestId: RequestId,
|
||||
slotIndex: UInt256,
|
||||
proof: seq[byte]) {.async.} =
|
||||
proof: seq[byte],
|
||||
collateral: UInt256) {.async.} =
|
||||
await market.approveFunds(collateral)
|
||||
await market.contract.fillSlot(requestId, slotIndex, proof)
|
||||
|
||||
method withdrawFunds(market: OnChainMarket,
|
||||
|
|
|
@ -38,10 +38,6 @@ proc slashMisses*(marketplace: Marketplace): UInt256 {.contract, view.}
|
|||
proc slashPercentage*(marketplace: Marketplace): UInt256 {.contract, view.}
|
||||
proc minCollateralThreshold*(marketplace: Marketplace): UInt256 {.contract, view.}
|
||||
|
||||
proc deposit*(marketplace: Marketplace, amount: UInt256) {.contract.}
|
||||
proc withdraw*(marketplace: Marketplace) {.contract.}
|
||||
proc balanceOf*(marketplace: Marketplace, account: Address): UInt256 {.contract, view.}
|
||||
|
||||
proc requestStorage*(marketplace: Marketplace, request: StorageRequest) {.contract.}
|
||||
proc fillSlot*(marketplace: Marketplace, requestId: RequestId, slotIndex: UInt256, proof: seq[byte]) {.contract.}
|
||||
proc withdrawFunds*(marketplace: Marketplace, requestId: RequestId) {.contract.}
|
||||
|
|
|
@ -19,6 +19,7 @@ type
|
|||
duration*: UInt256
|
||||
proofProbability*: UInt256
|
||||
reward*: UInt256
|
||||
collateral*: UInt256
|
||||
maxSlotLoss*: uint64
|
||||
StorageContent* = object
|
||||
cid*: string
|
||||
|
@ -84,7 +85,8 @@ func fromTuple(_: type StorageAsk, tupl: tuple): StorageAsk =
|
|||
duration: tupl[2],
|
||||
proofProbability: tupl[3],
|
||||
reward: tupl[4],
|
||||
maxSlotLoss: tupl[5]
|
||||
collateral: tupl[5],
|
||||
maxSlotLoss: tupl[6]
|
||||
)
|
||||
|
||||
func fromTuple(_: type StorageContent, tupl: tuple): StorageContent =
|
||||
|
|
|
@ -19,10 +19,6 @@ type
|
|||
OnRequestCancelled* = proc(requestId: RequestId) {.gcsafe, upraises:[].}
|
||||
OnRequestFailed* = proc(requestId: RequestId) {.gcsafe, upraises:[].}
|
||||
|
||||
method approveFunds*(market: Market, amount: UInt256) {.base, async.} =
|
||||
## This is generally needed for On-Chain ERC20 functionality to approve funds transfer to marketplace contract
|
||||
raiseAssert("not implemented")
|
||||
|
||||
method getSigner*(market: Market): Future[Address] {.base, async.} =
|
||||
raiseAssert("not implemented")
|
||||
|
||||
|
@ -67,7 +63,8 @@ method getActiveSlot*(
|
|||
method fillSlot*(market: Market,
|
||||
requestId: RequestId,
|
||||
slotIndex: UInt256,
|
||||
proof: seq[byte]) {.base, async.} =
|
||||
proof: seq[byte],
|
||||
collateral: UInt256) {.base, async.} =
|
||||
raiseAssert("not implemented")
|
||||
|
||||
method withdrawFunds*(market: Market,
|
||||
|
|
|
@ -244,6 +244,7 @@ proc requestStorage*(self: CodexNodeRef,
|
|||
nodes: uint,
|
||||
tolerance: uint,
|
||||
reward: UInt256,
|
||||
collateral: UInt256,
|
||||
expiry = UInt256.none): Future[?!PurchaseId] {.async.} =
|
||||
## Initiate a request for storage sequence, this might
|
||||
## be a multistep procedure.
|
||||
|
@ -288,6 +289,7 @@ proc requestStorage*(self: CodexNodeRef,
|
|||
duration: duration,
|
||||
proofProbability: proofProbability,
|
||||
reward: reward,
|
||||
collateral: collateral,
|
||||
maxSlotLoss: tolerance
|
||||
),
|
||||
content: StorageContent(
|
||||
|
|
|
@ -10,7 +10,6 @@ method enterAsync(state: PurchasePending) {.async.} =
|
|||
raiseAssert "invalid state"
|
||||
|
||||
try:
|
||||
await purchase.market.approveFunds(request.price())
|
||||
await purchase.market.requestStorage(request)
|
||||
except CatchableError as error:
|
||||
state.switch(PurchaseErrored(error: error))
|
||||
|
|
|
@ -140,6 +140,7 @@ proc initRestApi*(node: CodexNodeRef, conf: CodexConf): RestRouter =
|
|||
## expiry - timestamp, in seconds, when the request expires if the Request does not find requested amount of nodes to host the data
|
||||
## nodes - minimal number of nodes the content should be stored on
|
||||
## tolerance - allowed number of nodes that can be lost before pronouncing the content lost
|
||||
## colateral - requested collateral from hosts when they fill slot
|
||||
|
||||
without cid =? cid.tryGet.catch, error:
|
||||
return RestApiResponse.error(Http400, error.msg)
|
||||
|
@ -159,6 +160,7 @@ proc initRestApi*(node: CodexNodeRef, conf: CodexConf): RestRouter =
|
|||
nodes,
|
||||
tolerance,
|
||||
params.reward,
|
||||
params.collateral,
|
||||
params.expiry), error:
|
||||
|
||||
return RestApiResponse.error(Http500, error.msg)
|
||||
|
@ -269,9 +271,10 @@ proc initRestApi*(node: CodexNodeRef, conf: CodexConf): RestRouter =
|
|||
"/api/codex/v1/sales/availability") do () -> RestApiResponse:
|
||||
## Add available storage to sell
|
||||
##
|
||||
## size - size of available storage in bytes
|
||||
## duration - maximum time the storage should be sold for (in seconds)
|
||||
## minPrice - minimum price to be paid (in amount of tokens)
|
||||
## size - size of available storage in bytes
|
||||
## duration - maximum time the storage should be sold for (in seconds)
|
||||
## minPrice - minimum price to be paid (in amount of tokens)
|
||||
## maxCollateral - maximum collateral user is willing to pay per filled Slot (in amount of tokens)
|
||||
|
||||
without contracts =? node.contracts.host:
|
||||
return RestApiResponse.error(Http503, "Sales unavailable")
|
||||
|
|
|
@ -10,6 +10,7 @@ type
|
|||
duration*: UInt256
|
||||
proofProbability*: UInt256
|
||||
reward*: UInt256
|
||||
collateral*: UInt256
|
||||
expiry*: ?UInt256
|
||||
nodes*: ?uint
|
||||
tolerance*: ?uint
|
||||
|
@ -19,7 +20,8 @@ proc fromJson*(_: type Availability, bytes: seq[byte]): ?!Availability =
|
|||
let size = ?catch UInt256.fromHex(json["size"].getStr)
|
||||
let duration = ?catch UInt256.fromHex(json["duration"].getStr)
|
||||
let minPrice = ?catch UInt256.fromHex(json["minPrice"].getStr)
|
||||
success Availability.init(size, duration, minPrice)
|
||||
let maxCollateral = ?catch UInt256.fromHex(json["maxCollateral"].getStr)
|
||||
success Availability.init(size, duration, minPrice, maxCollateral)
|
||||
|
||||
proc fromJson*(_: type StorageRequestParams,
|
||||
bytes: seq[byte]): ?! StorageRequestParams =
|
||||
|
@ -27,6 +29,7 @@ proc fromJson*(_: type StorageRequestParams,
|
|||
let duration = ?catch UInt256.fromHex(json["duration"].getStr)
|
||||
let proofProbability = ?catch UInt256.fromHex(json["proofProbability"].getStr)
|
||||
let reward = ?catch UInt256.fromHex(json["reward"].getStr)
|
||||
let collateral = ?catch UInt256.fromHex(json["collateral"].getStr)
|
||||
let expiry = UInt256.fromHex(json["expiry"].getStr).catch.option
|
||||
let nodes = strutils.fromHex[uint](json["nodes"].getStr).catch.option
|
||||
let tolerance = strutils.fromHex[uint](json["tolerance"].getStr).catch.option
|
||||
|
@ -34,6 +37,7 @@ proc fromJson*(_: type StorageRequestParams,
|
|||
duration: duration,
|
||||
proofProbability: proofProbability,
|
||||
reward: reward,
|
||||
collateral: collateral,
|
||||
expiry: expiry,
|
||||
nodes: nodes,
|
||||
tolerance: tolerance
|
||||
|
|
|
@ -38,6 +38,7 @@ type
|
|||
size*: UInt256
|
||||
duration*: UInt256
|
||||
minPrice*: UInt256
|
||||
maxCollateral*: UInt256
|
||||
used*: bool
|
||||
Reservations* = ref object
|
||||
repo: RepoStore
|
||||
|
@ -67,11 +68,12 @@ proc init*(
|
|||
_: type Availability,
|
||||
size: UInt256,
|
||||
duration: UInt256,
|
||||
minPrice: UInt256): Availability =
|
||||
minPrice: UInt256,
|
||||
maxCollateral: UInt256): Availability =
|
||||
|
||||
var id: array[32, byte]
|
||||
doAssert randomBytes(id) == 32
|
||||
Availability(id: AvailabilityId(id), size: size, duration: duration, minPrice: minPrice)
|
||||
Availability(id: AvailabilityId(id), size: size, duration: duration, minPrice: minPrice, maxCollateral: maxCollateral)
|
||||
|
||||
func toArray*(id: AvailabilityId): array[32, byte] =
|
||||
array[32, byte](id)
|
||||
|
@ -81,6 +83,7 @@ proc `==`*(x, y: Availability): bool =
|
|||
x.id == y.id and
|
||||
x.size == y.size and
|
||||
x.duration == y.duration and
|
||||
x.maxCollateral == y.maxCollateral and
|
||||
x.minPrice == y.minPrice
|
||||
|
||||
proc `$`*(id: AvailabilityId): string = id.toArray.toHex
|
||||
|
@ -318,7 +321,7 @@ proc unused*(r: Reservations): Future[?!seq[Availability]] {.async.} =
|
|||
|
||||
proc find*(
|
||||
self: Reservations,
|
||||
size, duration, minPrice: UInt256,
|
||||
size, duration, minPrice: UInt256, collateral: UInt256,
|
||||
used: bool): Future[?Availability] {.async.} =
|
||||
|
||||
|
||||
|
@ -332,13 +335,15 @@ proc find*(
|
|||
if used == availability.used and
|
||||
size <= availability.size and
|
||||
duration <= availability.duration and
|
||||
collateral <= availability.maxCollateral and
|
||||
minPrice >= availability.minPrice:
|
||||
|
||||
trace "availability matched",
|
||||
used, availUsed = availability.used,
|
||||
size, availsize = availability.size,
|
||||
duration, availDuration = availability.duration,
|
||||
minPrice, availMinPrice = availability.minPrice
|
||||
minPrice, availMinPrice = availability.minPrice,
|
||||
collateral, availMaxCollateral = availability.maxCollateral
|
||||
|
||||
return some availability
|
||||
|
||||
|
@ -346,4 +351,5 @@ proc find*(
|
|||
used, availUsed = availability.used,
|
||||
size, availsize = availability.size,
|
||||
duration, availDuration = availability.duration,
|
||||
minPrice, availMinPrice = availability.minPrice
|
||||
minPrice, availMinPrice = availability.minPrice,
|
||||
collateral, availMaxCollateral = availability.maxCollateral
|
||||
|
|
|
@ -50,6 +50,7 @@ method run*(state: SaleDownloading, machine: Machine): Future[?State] {.async.}
|
|||
request.ask.slotSize,
|
||||
request.ask.duration,
|
||||
request.ask.pricePerSlot,
|
||||
request.ask.collateral,
|
||||
used = false):
|
||||
info "no availability found for request, ignoring",
|
||||
slotSize = request.ask.slotSize,
|
||||
|
|
|
@ -25,5 +25,7 @@ method onSlotFilled*(state: SaleFilling, requestId: RequestId,
|
|||
method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
|
||||
let data = SalesAgent(machine).data
|
||||
let market = SalesAgent(machine).context.market
|
||||
without (collateral =? data.request.?ask.?collateral):
|
||||
raiseAssert "Request not set"
|
||||
|
||||
await market.fillSlot(data.requestId, data.slotIndex, state.proof)
|
||||
await market.fillSlot(data.requestId, data.slotIndex, state.proof, collateral)
|
||||
|
|
|
@ -112,6 +112,9 @@ components:
|
|||
minPrice:
|
||||
type: string
|
||||
description: Minimum price to be paid (in amount of tokens) as hexadecimal string
|
||||
maxCollateral:
|
||||
type: string
|
||||
description: Maximum collateral user is willing to pay per filled Slot (in amount of tokens)
|
||||
|
||||
StorageRequestCreation:
|
||||
type: object
|
||||
|
@ -119,6 +122,7 @@ components:
|
|||
- reward
|
||||
- duration
|
||||
- proofProbability
|
||||
- collateral
|
||||
properties:
|
||||
duration:
|
||||
$ref: "#/components/schemas/Duration"
|
||||
|
@ -134,6 +138,9 @@ components:
|
|||
type: number
|
||||
description: Additional number of nodes on top of the `nodes` property that can be lost before pronouncing the content lost
|
||||
default: 0
|
||||
collateral:
|
||||
type: string
|
||||
description: Hexadecimal encoded number that represents how much collateral is asked from hosts that wants to fill a slots
|
||||
|
||||
StorageAsk:
|
||||
type: object
|
||||
|
|
|
@ -57,5 +57,6 @@ proc example*(_: type Availability): Availability =
|
|||
Availability.init(
|
||||
size = uint16.example.u256,
|
||||
duration = uint16.example.u256,
|
||||
minPrice = uint64.example.u256
|
||||
minPrice = uint64.example.u256,
|
||||
maxCollateral = uint16.example.u256
|
||||
)
|
||||
|
|
|
@ -67,10 +67,10 @@ proc hash*(requestId: RequestId): Hash =
|
|||
proc new*(_: type MockMarket): MockMarket =
|
||||
let config = MarketplaceConfig(
|
||||
collateral: CollateralConfig(
|
||||
initialAmount: 100.u256,
|
||||
minimumAmount: 40.u256,
|
||||
slashCriterion: 3.u256,
|
||||
slashPercentage: 10.u256
|
||||
repairRewardPercentage: 10,
|
||||
maxNumberOfSlashes: 5,
|
||||
slashCriterion: 3,
|
||||
slashPercentage: 10
|
||||
),
|
||||
proofs: ProofConfig(
|
||||
period: 10.u256,
|
||||
|
@ -80,9 +80,6 @@ proc new*(_: type MockMarket): MockMarket =
|
|||
)
|
||||
MockMarket(signer: Address.example, config: config)
|
||||
|
||||
method approveFunds*(market: MockMarket, amount: UInt256) {.async.} =
|
||||
discard
|
||||
|
||||
method getSigner*(market: MockMarket): Future[Address] {.async.} =
|
||||
return market.signer
|
||||
|
||||
|
@ -182,7 +179,8 @@ proc fillSlot*(market: MockMarket,
|
|||
method fillSlot*(market: MockMarket,
|
||||
requestId: RequestId,
|
||||
slotIndex: UInt256,
|
||||
proof: seq[byte]) {.async.} =
|
||||
proof: seq[byte],
|
||||
collateral: UInt256) {.async.} =
|
||||
market.fillSlot(requestId, slotIndex, proof, market.signer)
|
||||
|
||||
method withdrawFunds*(market: MockMarket,
|
||||
|
|
|
@ -39,8 +39,8 @@ suite "Reservations module":
|
|||
check (await reservations.allAvailabilities()).len == 0
|
||||
|
||||
test "generates unique ids for storage availability":
|
||||
let availability1 = Availability.init(1.u256, 2.u256, 3.u256)
|
||||
let availability2 = Availability.init(1.u256, 2.u256, 3.u256)
|
||||
let availability1 = Availability.init(1.u256, 2.u256, 3.u256, 4.u256)
|
||||
let availability2 = Availability.init(1.u256, 2.u256, 3.u256, 4.u256)
|
||||
check availability1.id != availability2.id
|
||||
|
||||
test "can reserve available storage":
|
||||
|
@ -125,7 +125,7 @@ suite "Reservations module":
|
|||
check isOk await reservations.markUsed(availability.id)
|
||||
|
||||
without available =? await reservations.find(availability.size,
|
||||
availability.duration, availability.minPrice, used = true):
|
||||
availability.duration, availability.minPrice, availability.maxCollateral, used = true):
|
||||
|
||||
fail()
|
||||
|
||||
|
@ -133,13 +133,13 @@ suite "Reservations module":
|
|||
check isOk await reservations.reserve(availability)
|
||||
|
||||
without available =? await reservations.find(availability.size,
|
||||
availability.duration, availability.minPrice, used = false):
|
||||
availability.duration, availability.minPrice, availability.maxCollateral, used = false):
|
||||
|
||||
fail()
|
||||
|
||||
test "non-existant availability cannot be found":
|
||||
check isNone (await reservations.find(availability.size,
|
||||
availability.duration, availability.minPrice, used = false))
|
||||
availability.duration, availability.minPrice, availability.maxCollateral, used = false))
|
||||
|
||||
test "non-existant availability cannot be retrieved":
|
||||
let r = await reservations.get(availability.id)
|
||||
|
|
|
@ -38,7 +38,8 @@ suite "Sales":
|
|||
availability = Availability.init(
|
||||
size=100.u256,
|
||||
duration=60.u256,
|
||||
minPrice=600.u256
|
||||
minPrice=600.u256,
|
||||
maxCollateral=400.u256
|
||||
)
|
||||
request = StorageRequest(
|
||||
ask: StorageAsk(
|
||||
|
@ -46,6 +47,7 @@ suite "Sales":
|
|||
slotSize: 100.u256,
|
||||
duration: 60.u256,
|
||||
reward: 10.u256,
|
||||
collateral: 200.u256,
|
||||
),
|
||||
content: StorageContent(
|
||||
cid: "some cid"
|
||||
|
@ -134,6 +136,13 @@ suite "Sales":
|
|||
await market.requestStorage(request)
|
||||
check getAvailability().?used == success false
|
||||
|
||||
test "ignores request when asked collateral is too high":
|
||||
var tooBigCollateral = request
|
||||
tooBigCollateral.ask.collateral = availability.maxCollateral + 1
|
||||
check isOk await reservations.reserve(availability)
|
||||
await market.requestStorage(tooBigCollateral)
|
||||
check await wasIgnored()
|
||||
|
||||
test "retrieves and stores data locally":
|
||||
var storingRequest: StorageRequest
|
||||
var storingSlot: UInt256
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
import pkg/chronos
|
||||
import pkg/stint
|
||||
import codex/contracts
|
||||
import ./token
|
||||
import ../ethertest
|
||||
|
||||
ethersuite "Collateral":
|
||||
|
||||
let collateral = 100.u256
|
||||
|
||||
var marketplace: Marketplace
|
||||
var token: TestToken
|
||||
|
||||
setup:
|
||||
let deployment = Deployment.init()
|
||||
marketplace = Marketplace.new(!deployment.address(Marketplace), provider.getSigner())
|
||||
token = TestToken.new(!deployment.address(TestToken), provider.getSigner())
|
||||
await token.mint(accounts[0], 1000.u256)
|
||||
|
||||
test "increases collateral":
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.deposit(collateral)
|
||||
let balance = await marketplace.balanceOf(accounts[0])
|
||||
check balance == collateral
|
||||
|
||||
test "withdraws collateral":
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.deposit(collateral)
|
||||
let balanceBefore = await token.balanceOf(accounts[0])
|
||||
await marketplace.withdraw()
|
||||
let balanceAfter = await token.balanceOf(accounts[0])
|
||||
check (balanceAfter - balanceBefore) == collateral
|
|
@ -1,20 +1,19 @@
|
|||
import std/json
|
||||
import pkg/chronos
|
||||
import pkg/ethers/testing
|
||||
import pkg/ethers/erc20
|
||||
import codex/contracts
|
||||
import codex/storageproofs
|
||||
import ../ethertest
|
||||
import ./examples
|
||||
import ./time
|
||||
import ./token
|
||||
|
||||
ethersuite "Marketplace contracts":
|
||||
let proof = exampleProof()
|
||||
|
||||
var client, host: Signer
|
||||
var marketplace: Marketplace
|
||||
var token: TestToken
|
||||
var collateral: UInt256
|
||||
var token: Erc20Token
|
||||
var periodicity: Periodicity
|
||||
var request: StorageRequest
|
||||
var slotId: SlotId
|
||||
|
@ -29,13 +28,11 @@ ethersuite "Marketplace contracts":
|
|||
|
||||
let deployment = Deployment.init()
|
||||
marketplace = Marketplace.new(!deployment.address(Marketplace), provider.getSigner())
|
||||
token = TestToken.new(!deployment.address(TestToken), provider.getSigner())
|
||||
|
||||
await token.mint(await client.getAddress(), 1_000_000_000.u256)
|
||||
await token.mint(await host.getAddress(), 1000_000_000.u256)
|
||||
let tokenAddress = await marketplace.token()
|
||||
token = Erc20Token.new(tokenAddress, provider.getSigner())
|
||||
|
||||
let config = await marketplace.config()
|
||||
collateral = config.collateral.initialAmount
|
||||
periodicity = Periodicity(seconds: config.proofs.period)
|
||||
|
||||
request = StorageRequest.example
|
||||
|
@ -45,8 +42,7 @@ ethersuite "Marketplace contracts":
|
|||
await token.approve(marketplace.address, request.price)
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.deposit(collateral)
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
await marketplace.fillSlot(request.id, 0.u256, proof)
|
||||
slotId = request.slotId(0.u256)
|
||||
|
||||
|
@ -61,6 +57,7 @@ ethersuite "Marketplace contracts":
|
|||
|
||||
proc startContract() {.async.} =
|
||||
for slotIndex in 1..<request.ask.slots:
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
await marketplace.fillSlot(request.id, slotIndex.u256, proof)
|
||||
|
||||
test "accept marketplace proofs":
|
||||
|
@ -85,7 +82,7 @@ ethersuite "Marketplace contracts":
|
|||
let startBalance = await token.balanceOf(address)
|
||||
await marketplace.freeSlot(slotId)
|
||||
let endBalance = await token.balanceOf(address)
|
||||
check endBalance == (startBalance + request.ask.duration * request.ask.reward)
|
||||
check endBalance == (startBalance + request.ask.duration * request.ask.reward + request.ask.collateral)
|
||||
|
||||
test "cannot mark proofs missing for cancelled request":
|
||||
await provider.advanceTimeTo(request.expiry + 1)
|
||||
|
|
|
@ -6,14 +6,12 @@ import codex/storageproofs
|
|||
import ../ethertest
|
||||
import ./examples
|
||||
import ./time
|
||||
import ./token
|
||||
|
||||
ethersuite "On-Chain Market":
|
||||
let proof = exampleProof()
|
||||
|
||||
var market: OnChainMarket
|
||||
var marketplace: Marketplace
|
||||
var token: TestToken
|
||||
var request: StorageRequest
|
||||
var slotIndex: UInt256
|
||||
var periodicity: Periodicity
|
||||
|
@ -21,13 +19,7 @@ ethersuite "On-Chain Market":
|
|||
setup:
|
||||
let deployment = Deployment.init()
|
||||
marketplace = Marketplace.new(!deployment.address(Marketplace), provider.getSigner())
|
||||
token = TestToken.new(!deployment.address(TestToken), provider.getSigner())
|
||||
await token.mint(accounts[0], 1_000_000_000.u256)
|
||||
|
||||
let config = await marketplace.config()
|
||||
let collateral = config.collateral.initialAmount
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.deposit(collateral)
|
||||
|
||||
market = OnChainMarket.new(marketplace)
|
||||
periodicity = Periodicity(seconds: config.proofs.period)
|
||||
|
@ -55,18 +47,15 @@ ethersuite "On-Chain Market":
|
|||
check (await market.getSigner()) == (await provider.getSigner().getAddress())
|
||||
|
||||
test "supports marketplace requests":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
|
||||
test "can retrieve previously submitted requests":
|
||||
check (await market.getRequest(request.id)) == none StorageRequest
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
let r = await market.getRequest(request.id)
|
||||
check (r) == some request
|
||||
|
||||
test "supports withdrawing of funds":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
await provider.advanceTimeTo(request.expiry)
|
||||
await market.withdrawFunds(request.id)
|
||||
|
@ -78,26 +67,22 @@ ethersuite "On-Chain Market":
|
|||
receivedIds.add(id)
|
||||
receivedAsks.add(ask)
|
||||
let subscription = await market.subscribeRequests(onRequest)
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
check receivedIds == @[request.id]
|
||||
check receivedAsks == @[request.ask]
|
||||
await subscription.unsubscribe()
|
||||
|
||||
test "supports filling of slots":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
await market.fillSlot(request.id, slotIndex, proof)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
|
||||
test "can retrieve host that filled slot":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
check (await market.getHost(request.id, slotIndex)) == none Address
|
||||
await market.fillSlot(request.id, slotIndex, proof)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
check (await market.getHost(request.id, slotIndex)) == some accounts[0]
|
||||
|
||||
test "support slot filled subscriptions":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
var receivedIds: seq[RequestId]
|
||||
var receivedSlotIndices: seq[UInt256]
|
||||
|
@ -105,34 +90,32 @@ ethersuite "On-Chain Market":
|
|||
receivedIds.add(id)
|
||||
receivedSlotIndices.add(slotIndex)
|
||||
let subscription = await market.subscribeSlotFilled(request.id, slotIndex, onSlotFilled)
|
||||
await market.fillSlot(request.id, slotIndex, proof)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
check receivedIds == @[request.id]
|
||||
check receivedSlotIndices == @[slotIndex]
|
||||
await subscription.unsubscribe()
|
||||
|
||||
test "subscribes only to a certain slot":
|
||||
var otherSlot = slotIndex - 1
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
var receivedSlotIndices: seq[UInt256]
|
||||
proc onSlotFilled(requestId: RequestId, slotIndex: UInt256) =
|
||||
receivedSlotIndices.add(slotIndex)
|
||||
let subscription = await market.subscribeSlotFilled(request.id, slotIndex, onSlotFilled)
|
||||
await market.fillSlot(request.id, otherSlot, proof)
|
||||
await market.fillSlot(request.id, otherSlot, proof, request.ask.collateral)
|
||||
check receivedSlotIndices.len == 0
|
||||
await market.fillSlot(request.id, slotIndex, proof)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
check receivedSlotIndices == @[slotIndex]
|
||||
await subscription.unsubscribe()
|
||||
|
||||
test "support fulfillment subscriptions":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
var receivedIds: seq[RequestId]
|
||||
proc onFulfillment(id: RequestId) =
|
||||
receivedIds.add(id)
|
||||
let subscription = await market.subscribeFulfillment(request.id, onFulfillment)
|
||||
for slotIndex in 0..<request.ask.slots:
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof)
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
||||
check receivedIds == @[request.id]
|
||||
await subscription.unsubscribe()
|
||||
|
||||
|
@ -140,9 +123,7 @@ ethersuite "On-Chain Market":
|
|||
var otherRequest = StorageRequest.example
|
||||
otherRequest.client = accounts[0]
|
||||
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
await token.approve(marketplace.address, otherRequest.price)
|
||||
await market.requestStorage(otherRequest)
|
||||
|
||||
var receivedIds: seq[RequestId]
|
||||
|
@ -152,16 +133,15 @@ ethersuite "On-Chain Market":
|
|||
let subscription = await market.subscribeFulfillment(request.id, onFulfillment)
|
||||
|
||||
for slotIndex in 0..<request.ask.slots:
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof)
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
||||
for slotIndex in 0..<otherRequest.ask.slots:
|
||||
await market.fillSlot(otherRequest.id, slotIndex.u256, proof)
|
||||
await market.fillSlot(otherRequest.id, slotIndex.u256, proof, otherRequest.ask.collateral)
|
||||
|
||||
check receivedIds == @[request.id]
|
||||
|
||||
await subscription.unsubscribe()
|
||||
|
||||
test "support request cancelled subscriptions":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
|
||||
var receivedIds: seq[RequestId]
|
||||
|
@ -175,7 +155,6 @@ ethersuite "On-Chain Market":
|
|||
await subscription.unsubscribe()
|
||||
|
||||
test "support request failed subscriptions":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
|
||||
var receivedIds: seq[RequestId]
|
||||
|
@ -184,7 +163,7 @@ ethersuite "On-Chain Market":
|
|||
let subscription = await market.subscribeRequestFailed(request.id, onRequestFailed)
|
||||
|
||||
for slotIndex in 0..<request.ask.slots:
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof)
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
||||
for slotIndex in 0..request.ask.maxSlotLoss:
|
||||
let slotId = request.slotId(slotIndex.u256)
|
||||
while true:
|
||||
|
@ -201,9 +180,7 @@ ethersuite "On-Chain Market":
|
|||
test "subscribes only to a certain request cancellation":
|
||||
var otherRequest = request
|
||||
otherRequest.nonce = Nonce.example
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
await token.approve(marketplace.address, otherRequest.price)
|
||||
await market.requestStorage(otherRequest)
|
||||
|
||||
var receivedIds: seq[RequestId]
|
||||
|
@ -222,11 +199,9 @@ ethersuite "On-Chain Market":
|
|||
check isNone await market.getRequest(request.id)
|
||||
|
||||
test "can retrieve active requests":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
var request2 = StorageRequest.example
|
||||
request2.client = accounts[0]
|
||||
await token.approve(marketplace.address, request2.price)
|
||||
await market.requestStorage(request2)
|
||||
check (await market.myRequests()) == @[request.id, request2.id]
|
||||
|
||||
|
@ -234,31 +209,27 @@ ethersuite "On-Chain Market":
|
|||
check (await market.requestState(request.id)) == none RequestState
|
||||
|
||||
test "can retrieve request state":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
for slotIndex in 0..<request.ask.slots:
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof)
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
||||
check (await market.requestState(request.id)) == some RequestState.Started
|
||||
|
||||
test "can retrieve active slots":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
await market.fillSlot(request.id, slotIndex - 1, proof)
|
||||
await market.fillSlot(request.id, slotIndex, proof)
|
||||
await market.fillSlot(request.id, slotIndex - 1, proof, request.ask.collateral)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
let slotId1 = request.slotId(slotIndex - 1)
|
||||
let slotId2 = request.slotId(slotIndex)
|
||||
check (await market.mySlots()) == @[slotId1, slotId2]
|
||||
|
||||
test "returns none when slot is empty":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
let slotId = request.slotId(slotIndex)
|
||||
check (await market.getActiveSlot(slotId)) == none Slot
|
||||
|
||||
test "can retrieve request details from slot id":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
await market.fillSlot(request.id, slotIndex, proof)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
let slotId = request.slotId(slotIndex)
|
||||
let expected = Slot(request: request, slotIndex: slotIndex)
|
||||
check (await market.getActiveSlot(slotId)) == some expected
|
||||
|
@ -268,8 +239,7 @@ ethersuite "On-Chain Market":
|
|||
check (await market.slotState(slotId)) == SlotState.Free
|
||||
|
||||
test "retrieves correct slot state once filled":
|
||||
await token.approve(marketplace.address, request.price)
|
||||
await market.requestStorage(request)
|
||||
await market.fillSlot(request.id, slotIndex, proof)
|
||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
||||
let slotId = request.slotId(slotIndex)
|
||||
check (await market.slotState(slotId)) == SlotState.Filled
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import pkg/chronos
|
||||
import pkg/stint
|
||||
import pkg/ethers
|
||||
import pkg/ethers/erc20
|
||||
|
||||
type
|
||||
TestToken* = ref object of Erc20Token
|
||||
|
||||
proc mint*(token: TestToken, holder: Address, amount: UInt256) {.contract.}
|
|
@ -28,6 +28,7 @@ proc example*(_: type StorageRequest): StorageRequest =
|
|||
slots: 4,
|
||||
slotSize: (1 * 1024 * 1024 * 1024).u256, # 1 Gigabyte
|
||||
duration: (10 * 60 * 60).u256, # 10 hours
|
||||
collateral: 200.u256,
|
||||
proofProbability: 4.u256, # require a proof roughly once every 4 periods
|
||||
reward: 84.u256,
|
||||
maxSlotLoss: 2 # 2 slots can be freed without data considered to be lost
|
||||
|
|
|
@ -31,13 +31,15 @@ proc requestStorage*(client: CodexClient,
|
|||
duration: uint64,
|
||||
reward: uint64,
|
||||
proofProbability: uint64,
|
||||
expiry: UInt256): string =
|
||||
expiry: UInt256,
|
||||
collateral: uint64): string =
|
||||
let url = client.baseurl & "/storage/request/" & cid
|
||||
let json = %*{
|
||||
"duration": "0x" & duration.toHex,
|
||||
"reward": "0x" & reward.toHex,
|
||||
"proofProbability": "0x" & proofProbability.toHex,
|
||||
"expiry": "0x" & expiry.toHex
|
||||
"expiry": "0x" & expiry.toHex,
|
||||
"collateral": "0x" & collateral.toHex,
|
||||
}
|
||||
let response = client.http.post(url, $json)
|
||||
assert response.status == "200 OK"
|
||||
|
@ -49,12 +51,13 @@ proc getPurchase*(client: CodexClient, purchase: string): JsonNode =
|
|||
parseJson(body).catch |? nil
|
||||
|
||||
proc postAvailability*(client: CodexClient,
|
||||
size, duration, minPrice: uint64): JsonNode =
|
||||
size, duration, minPrice: uint64, maxCollateral: uint64): JsonNode =
|
||||
let url = client.baseurl & "/sales/availability"
|
||||
let json = %*{
|
||||
"size": "0x" & size.toHex,
|
||||
"duration": "0x" & duration.toHex,
|
||||
"minPrice": "0x" & minPrice.toHex
|
||||
"minPrice": "0x" & minPrice.toHex,
|
||||
"maxCollateral": "0x" & maxCollateral.toHex
|
||||
}
|
||||
let response = client.http.post(url, $json)
|
||||
assert response.status == "200 OK"
|
||||
|
|
|
@ -4,14 +4,14 @@ import pkg/stint
|
|||
import ../contracts/time
|
||||
import ../codex/helpers/eventually
|
||||
import ./twonodes
|
||||
import ./tokens
|
||||
|
||||
twonodessuite "Integration tests", debug1 = false, debug2 = false:
|
||||
|
||||
setup:
|
||||
await provider.getSigner(accounts[0]).mint()
|
||||
await provider.getSigner(accounts[1]).mint()
|
||||
await provider.getSigner(accounts[1]).deposit()
|
||||
# Our Hardhat configuration does use automine, which means that time tracked by `provider.currentTime()` is not
|
||||
# advanced until blocks are mined and that happens only when transaction is submitted.
|
||||
# As we use in tests provider.currentTime() which uses block timestamp this can lead to synchronization issues.
|
||||
await provider.advanceTime(1.u256)
|
||||
|
||||
test "nodes can print their peer information":
|
||||
check client1.info() != client2.info()
|
||||
|
@ -25,25 +25,25 @@ twonodessuite "Integration tests", debug1 = false, debug2 = false:
|
|||
check cid1 != cid2
|
||||
|
||||
test "node handles new storage availability":
|
||||
let availability1 = client1.postAvailability(size=1, duration=2, minPrice=3)
|
||||
let availability2 = client1.postAvailability(size=4, duration=5, minPrice=6)
|
||||
let availability1 = client1.postAvailability(size=1, duration=2, minPrice=3, maxCollateral=4)
|
||||
let availability2 = client1.postAvailability(size=4, duration=5, minPrice=6, maxCollateral=7)
|
||||
check availability1 != availability2
|
||||
|
||||
test "node lists storage that is for sale":
|
||||
let availability = client1.postAvailability(size=1, duration=2, minPrice=3)
|
||||
let availability = client1.postAvailability(size=1, duration=2, minPrice=3, maxCollateral=4)
|
||||
check availability in client1.getAvailabilities()
|
||||
|
||||
test "node handles storage request":
|
||||
let expiry = (await provider.currentTime()) + 30
|
||||
let cid = client1.upload("some file contents")
|
||||
let id1 = client1.requestStorage(cid, duration=1, reward=2, proofProbability=3, expiry=expiry)
|
||||
let id2 = client1.requestStorage(cid, duration=4, reward=5, proofProbability=6, expiry=expiry)
|
||||
let id1 = client1.requestStorage(cid, duration=1, reward=2, proofProbability=3, expiry=expiry, collateral=200)
|
||||
let id2 = client1.requestStorage(cid, duration=4, reward=5, proofProbability=6, expiry=expiry, collateral=201)
|
||||
check id1 != id2
|
||||
|
||||
test "node retrieves purchase status":
|
||||
let expiry = (await provider.currentTime()) + 30
|
||||
let cid = client1.upload("some file contents")
|
||||
let id = client1.requestStorage(cid, duration=1, reward=2, proofProbability=3, expiry=expiry)
|
||||
let id = client1.requestStorage(cid, duration=1, reward=2, proofProbability=3, expiry=expiry, collateral=200)
|
||||
let purchase = client1.getPurchase(id)
|
||||
check purchase{"request"}{"ask"}{"duration"} == %"0x1"
|
||||
check purchase{"request"}{"ask"}{"reward"} == %"0x2"
|
||||
|
@ -52,7 +52,7 @@ twonodessuite "Integration tests", debug1 = false, debug2 = false:
|
|||
test "node remembers purchase status after restart":
|
||||
let expiry = (await provider.currentTime()) + 30
|
||||
let cid = client1.upload("some file contents")
|
||||
let id = client1.requestStorage(cid, duration=1, reward=2, proofProbability=3, expiry=expiry)
|
||||
let id = client1.requestStorage(cid, duration=1, reward=2, proofProbability=3, expiry=expiry, collateral=200)
|
||||
check eventually client1.getPurchase(id){"state"}.getStr() == "submitted"
|
||||
|
||||
node1.restart()
|
||||
|
@ -65,12 +65,12 @@ twonodessuite "Integration tests", debug1 = false, debug2 = false:
|
|||
test "nodes negotiate contracts on the marketplace":
|
||||
let size: uint64 = 0xFFFFF
|
||||
# client 2 makes storage available
|
||||
discard client2.postAvailability(size=size, duration=200, minPrice=300)
|
||||
discard client2.postAvailability(size=size, duration=200, minPrice=300, maxCollateral=300)
|
||||
|
||||
# client 1 requests storage
|
||||
let expiry = (await provider.currentTime()) + 30
|
||||
let cid = client1.upload("some file contents")
|
||||
let purchase = client1.requestStorage(cid, duration=100, reward=400, proofProbability=3, expiry=expiry)
|
||||
let purchase = client1.requestStorage(cid, duration=100, reward=400, proofProbability=3, expiry=expiry, collateral=200)
|
||||
|
||||
check eventually client1.getPurchase(purchase){"state"} == %"started"
|
||||
check client1.getPurchase(purchase){"error"} == newJNull()
|
||||
|
|
|
@ -3,7 +3,6 @@ import codex/contracts/deployment
|
|||
import ../contracts/time
|
||||
import ../codex/helpers/eventually
|
||||
import ./twonodes
|
||||
import ./tokens
|
||||
|
||||
twonodessuite "Proving integration test", debug1=false, debug2=false:
|
||||
|
||||
|
@ -14,15 +13,17 @@ twonodessuite "Proving integration test", debug1=false, debug2=false:
|
|||
let deployment = Deployment.init()
|
||||
marketplace = Marketplace.new(!deployment.address(Marketplace), provider)
|
||||
config = await marketplace.config()
|
||||
await provider.getSigner(accounts[0]).mint()
|
||||
await provider.getSigner(accounts[1]).mint()
|
||||
await provider.getSigner(accounts[1]).deposit()
|
||||
|
||||
# Our Hardhat configuration does use automine, which means that time tracked by `provider.currentTime()` is not
|
||||
# advanced until blocks are mined and that happens only when transaction is submitted.
|
||||
# As we use in tests provider.currentTime() which uses block timestamp this can lead to synchronization issues.
|
||||
await provider.advanceTime(1.u256)
|
||||
|
||||
proc waitUntilPurchaseIsStarted {.async.} =
|
||||
discard client2.postAvailability(size=0xFFFFF, duration=200, minPrice=300)
|
||||
discard client2.postAvailability(size=0xFFFFF, duration=200, minPrice=300, maxCollateral=200)
|
||||
let expiry = (await provider.currentTime()) + 30
|
||||
let cid = client1.upload("some file contents")
|
||||
let purchase = client1.requestStorage(cid, duration=100, reward=400, proofProbability=3, expiry=expiry)
|
||||
let purchase = client1.requestStorage(cid, duration=100, reward=400, proofProbability=3, expiry=expiry, collateral=100)
|
||||
check eventually client1.getPurchase(purchase){"state"} == %"started"
|
||||
|
||||
test "hosts submit periodic proofs for slots they fill":
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import pkg/ethers/erc20
|
||||
import codex/contracts
|
||||
import ../contracts/token
|
||||
|
||||
proc mint*(signer: Signer, amount = 1_000_000.u256) {.async.} =
|
||||
## Mints a considerable amount of tokens and approves them for transfer to
|
||||
## the Marketplace contract.
|
||||
let deployment = Deployment.init()
|
||||
let token = TestToken.new(!deployment.address(TestToken), signer)
|
||||
let marketplace = Marketplace.new(!deployment.address(Marketplace), signer)
|
||||
await token.mint(await signer.getAddress(), amount)
|
||||
|
||||
proc deposit*(signer: Signer) {.async.} =
|
||||
## Deposits sufficient collateral into the Marketplace contract.
|
||||
let deployment = Deployment.init()
|
||||
let marketplace = Marketplace.new(!deployment.address(Marketplace), signer)
|
||||
let config = await marketplace.config()
|
||||
let tokenAddress = await marketplace.token()
|
||||
let token = Erc20Token.new(tokenAddress, signer)
|
||||
|
||||
await token.approve(marketplace.address, config.collateral.initialAmount)
|
||||
await marketplace.deposit(config.collateral.initialAmount)
|
|
@ -1,4 +1,3 @@
|
|||
import ./contracts/testCollateral
|
||||
import ./contracts/testContracts
|
||||
import ./contracts/testMarket
|
||||
import ./contracts/testProofs
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit fb76f7d0b2f94914b00f2a0f4136ebfb27df6abc
|
||||
Subproject commit 6e66abbfcd9be6cbd0434dc5a80f1419c66a914e
|
|
@ -1 +1 @@
|
|||
Subproject commit 6cbbda7e4d009e02d0583b325b31dc68dff27854
|
||||
Subproject commit 30e4184a99c8c1ba329925912d2c5d4b09acf8cc
|
Loading…
Reference in New Issue