Skip to content

Commit

Permalink
devnet -> testnet (#302)
Browse files Browse the repository at this point in the history
  • Loading branch information
arielmelendez authored Dec 28, 2024
2 parents a11dac5 + 7bbe777 commit 029a449
Show file tree
Hide file tree
Showing 4 changed files with 389 additions and 55 deletions.
228 changes: 197 additions & 31 deletions spec/gar_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1129,9 +1129,11 @@ describe("gar", function()
weights = testGateway.weights,
}, result)
end)
end)

it("should decrease delegated stake if the remaining stake is greater than the minimum stake", function()
local totalDelegatedStake = 750000000
describe("decreaseDelegateStake", function()
it("should decrease delegated stake if the remaining stake is at least the minimum stake", function()
local totalDelegatedStake = minDelegatedStake + 100000000
local decreaseAmount = 100000000
_G.GatewayRegistry[stubGatewayAddress] = testGateway
_G.GatewayRegistry[stubGatewayAddress].totalDelegatedStake = totalDelegatedStake
Expand All @@ -1141,13 +1143,13 @@ describe("gar", function()
vaults = {},
}

local expectation = {
local expectedGateway = {
operatorStake = testGateway.operatorStake,
totalDelegatedStake = totalDelegatedStake - decreaseAmount,
totalDelegatedStake = minDelegatedStake,
vaults = {},
delegates = {
[stubRandomAddress] = {
delegatedStake = totalDelegatedStake - decreaseAmount,
delegatedStake = minDelegatedStake,
startTimestamp = 0,
vaults = {
[stubMessageId] = {
Expand All @@ -1166,6 +1168,25 @@ describe("gar", function()
observerAddress = testGateway.observerAddress,
weights = testGateway.weights,
}

local expectation = {
amountWithdrawn = 0,
delegatePruned = false,
expeditedWithdrawalFee = 0,
gatewayTotalDelegatedStake = minDelegatedStake,
penaltyRate = 0,
updatedDelegate = {
delegatedStake = minDelegatedStake,
startTimestamp = 0,
vaults = {
[stubMessageId] = {
balance = decreaseAmount,
startTimestamp = startTimestamp,
endTimestamp = startTimestamp + (90 * 24 * 60 * 60 * 1000), -- 90 days
},
},
},
}
local status, result = pcall(
gar.decreaseDelegateStake,
stubGatewayAddress,
Expand All @@ -1175,58 +1196,203 @@ describe("gar", function()
stubMessageId
)
assert.is_true(status)
assert.are.same(expectation, result.gateway)
assert.are.same(expectation, _G.GatewayRegistry[stubGatewayAddress])
assert.are.same(expectation, result)
assert.are.same(expectedGateway, _G.GatewayRegistry[stubGatewayAddress])
end)

it("should decrease delegated stake with instant withdrawal and apply penalty and remove delegate", function()
_G.Balances[ao.id] = 0
local expeditedWithdrawalFee = 1000 * 0.50
local withdrawalAmount = 1000 - expeditedWithdrawalFee
_G.GatewayRegistry[stubGatewayAddress] = {
operatorStake = minOperatorStake,
totalDelegatedStake = minDelegatedStake + 1000,
it(
"should decrease delegated stake with instant withdrawal if the remaining stake is at least the minimum stake",
function()
local totalDelegatedStake = minDelegatedStake + 100000000
local decreaseAmount = 100000000
local expeditedWithdrawalFee = decreaseAmount * 0.50
local withdrawalAmount = decreaseAmount - expeditedWithdrawalFee
_G.GatewayRegistry[stubGatewayAddress] = testGateway
_G.GatewayRegistry[stubGatewayAddress].totalDelegatedStake = totalDelegatedStake
_G.GatewayRegistry[stubGatewayAddress].delegates[stubRandomAddress] = {
delegatedStake = totalDelegatedStake,
startTimestamp = 0,
vaults = {},
}

local expectedGateway = {
operatorStake = testGateway.operatorStake,
totalDelegatedStake = minDelegatedStake,
vaults = {},
delegates = {
[stubRandomAddress] = {
delegatedStake = minDelegatedStake,
startTimestamp = 0,
vaults = {},
},
},
startTimestamp = testGateway.startTimestamp,
stats = testGateway.stats,
services = testGateway.services,
settings = testGateway.settings,
status = testGateway.status,
observerAddress = testGateway.observerAddress,
weights = testGateway.weights,
}

local expectation = {
amountWithdrawn = withdrawalAmount,
delegatePruned = false,
expeditedWithdrawalFee = expeditedWithdrawalFee,
gatewayTotalDelegatedStake = minDelegatedStake,
penaltyRate = constants.MAX_EXPEDITED_WITHDRAWAL_PENALTY_RATE,
updatedDelegate = {
delegatedStake = minDelegatedStake,
startTimestamp = 0,
vaults = {},
},
}
local status, result = pcall(
gar.decreaseDelegateStake,
stubGatewayAddress,
stubRandomAddress,
decreaseAmount,
startTimestamp,
stubMessageId,
true -- Instant withdrawal flag
)
assert.is_true(status)
assert.are.same(expectation, result)
assert.are.same(expectedGateway, _G.GatewayRegistry[stubGatewayAddress])
assert.are.equal(withdrawalAmount, _G.Balances[stubRandomAddress])
assert.are.equal(expeditedWithdrawalFee, _G.Balances[ao.id])
end
)

it("should allow decreasing entire delegated stake", function()
local totalDelegatedStake = minDelegatedStake
local decreaseAmount = minDelegatedStake
_G.GatewayRegistry[stubGatewayAddress] = testGateway
_G.GatewayRegistry[stubGatewayAddress].totalDelegatedStake = totalDelegatedStake
_G.GatewayRegistry[stubGatewayAddress].delegates[stubRandomAddress] = {
delegatedStake = totalDelegatedStake,
startTimestamp = 0,
vaults = {},
startTimestamp = startTimestamp,
}

local expectedGateway = {
operatorStake = testGateway.operatorStake,
totalDelegatedStake = 0,
vaults = {},
delegates = {
[stubRandomAddress] = {
delegatedStake = 0,
startTimestamp = 0,
vaults = {
[stubMessageId] = {
balance = decreaseAmount,
startTimestamp = startTimestamp,
endTimestamp = startTimestamp + (90 * 24 * 60 * 60 * 1000), -- 90 days
},
},
},
},
startTimestamp = testGateway.startTimestamp,
stats = testGateway.stats,
services = testGateway.services,
settings = testGateway.settings,
status = testGateway.status,
observerAddress = testGateway.observerAddress,
delegates = {
[stubRandomAddress] = {
delegatedStake = minDelegatedStake + 1000,
startTimestamp = 0,
vaults = {},
weights = testGateway.weights,
}

local expectation = {
amountWithdrawn = 0,
delegatePruned = false,
expeditedWithdrawalFee = 0,
gatewayTotalDelegatedStake = 0,
penaltyRate = 0,
updatedDelegate = {
delegatedStake = 0,
startTimestamp = 0,
vaults = {
[stubMessageId] = {
balance = decreaseAmount,
startTimestamp = startTimestamp,
endTimestamp = startTimestamp + (90 * 24 * 60 * 60 * 1000), -- 90 days
},
},
},
}
local status, result = pcall(
gar.decreaseDelegateStake,
stubGatewayAddress,
stubRandomAddress,
1000,
decreaseAmount,
startTimestamp,
stubMessageId,
true -- instant withdrawal
stubMessageId
)
assert.is_true(status)
assert.are.same(expectation, result)
assert.are.same(expectedGateway, _G.GatewayRegistry[stubGatewayAddress])
end)

it("should allow decreasing entire delegated stake with instant withdrawal and prune the delegate", function()
local totalDelegatedStake = minDelegatedStake
local decreaseAmount = minDelegatedStake
local expeditedWithdrawalFee = decreaseAmount * 0.50
local withdrawalAmount = decreaseAmount - expeditedWithdrawalFee
_G.GatewayRegistry[stubGatewayAddress] = testGateway
_G.GatewayRegistry[stubGatewayAddress].totalDelegatedStake = totalDelegatedStake
_G.GatewayRegistry[stubGatewayAddress].delegates[stubRandomAddress] = {
delegatedStake = totalDelegatedStake,
startTimestamp = 0,
vaults = {},
}

local expectedGateway = {
operatorStake = testGateway.operatorStake,
totalDelegatedStake = 0,
vaults = {},
delegates = {},
startTimestamp = testGateway.startTimestamp,
stats = testGateway.stats,
services = testGateway.services,
settings = utils.deepCopy(testGateway.settings),
status = testGateway.status,
observerAddress = testGateway.observerAddress,
weights = testGateway.weights,
}
expectedGateway.settings.allowedDelegatesLookup["test-this-is-valid-arweave-wallet-address-3"] = true -- Add pruned delegate back to the allowlist

local expectation = {
amountWithdrawn = withdrawalAmount,
delegatePruned = true,
expeditedWithdrawalFee = expeditedWithdrawalFee,
gatewayTotalDelegatedStake = 0,
penaltyRate = constants.MAX_EXPEDITED_WITHDRAWAL_PENALTY_RATE,
updatedDelegate = {
delegatedStake = 0,
startTimestamp = 0,
vaults = {},
},
}
local status, result = pcall(
gar.decreaseDelegateStake,
stubGatewayAddress,
stubRandomAddress,
decreaseAmount,
startTimestamp,
stubMessageId,
true -- Instant withdrawal flag
)
assert.is_true(status)
assert.are.same(result.gateway.delegates[stubRandomAddress].delegatedStake, minDelegatedStake)
assert.are.equal(result.gateway.totalDelegatedStake, minDelegatedStake)
assert.are.equal(withdrawalAmount, result.amountWithdrawn)
assert.are.same(expectation, result)
assert.are.same(expectedGateway, _G.GatewayRegistry[stubGatewayAddress])
assert.are.equal(withdrawalAmount, _G.Balances[stubRandomAddress])
assert.are.equal(expeditedWithdrawalFee, result.expeditedWithdrawalFee)
assert.are.equal(expeditedWithdrawalFee, _G.Balances[ao.id])
assert.are.equal(constants.MAX_EXPEDITED_WITHDRAWAL_PENALTY_RATE, result.penaltyRate)
assert.are.equal(minDelegatedStake, _G.GatewayRegistry[stubGatewayAddress].totalDelegatedStake)
end)

it("should error if the remaining delegate stake is less than the minimum stake", function()
it("should error if the remaining delegate stake is less than the minimum stake and greater than 0", function()
local delegatedStake = minDelegatedStake
_G.GatewayRegistry[stubGatewayAddress] = {
operatorStake = minOperatorStake,
totalDelegatedStake = minDelegatedStake - 1,
totalDelegatedStake = minDelegatedStake,
vaults = {},
delegates = {
[stubRandomAddress] = {
Expand Down
34 changes: 29 additions & 5 deletions src/gar.lua
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ end
--- @param gateway Gateway
--- @param quantity mARIO
--- @param ban boolean|nil do not add the delegate back to the gateway allowlist if their delegation is over
--- @return Delegate, boolean # a copy of the updated delegate and whether or not it was pruned
function decreaseDelegateStakeAtGateway(delegateAddress, gateway, quantity, ban)
local delegate = gateway.delegates[delegateAddress]
assert(delegate, "Delegate is required")
Expand All @@ -505,10 +506,11 @@ function decreaseDelegateStakeAtGateway(delegateAddress, gateway, quantity, ban)
assert(gateway, "Gateway is required")
delegate.delegatedStake = delegate.delegatedStake - quantity
gateway.totalDelegatedStake = gateway.totalDelegatedStake - quantity
gar.pruneDelegateFromGatewayIfNecessary(delegateAddress, gateway)
local pruned = gar.pruneDelegateFromGatewayIfNecessary(delegateAddress, gateway)
if ban and gateway.settings.allowedDelegatesLookup then
gateway.settings.allowedDelegatesLookup[delegateAddress] = nil
end
return utils.deepCopy(delegate), pruned
end

--- Creates a delegate at a gateway, managing allowlisting accounting if necessary
Expand Down Expand Up @@ -615,6 +617,21 @@ function gar.getSettingsUnsafe()
return GatewayRegistrySettings
end

--- @class DecreaseDelegateStakeReturn
--- @field gatewayTotalDelegatedStake mARIO The updated amount of total delegated stake at the gateway
--- @field updatedDelegate Delegate The updated delegate object
--- @field delegatePruned boolean Whether or not the delegate was pruned from the gateway
--- @field penaltyRate number The penalty rate for the expedited withdrawal, if applicable
--- @field expeditedWithdrawalFee number The fee deducted from the stake for the expedited withdrawal, if applicable
--- @field amountWithdrawn number The amount of stake withdrawn after any penalty fee is deducted

--- @param gatewayAddress WalletAddress The address of the gateway from which to decrease delegated stake
--- @param delegator WalletAddress The address of the delegator for which to decrease delegated stake
--- @param qty mARIO The amount of delegated stake to decrease
--- @param currentTimestamp Timestamp The current timestamp
--- @param messageId MessageId The message ID of the current action
--- @param instantWithdraw boolean Whether to withdraw the stake instantly; otherwise allow it to be vaulted
--- @return DecreaseDelegateStakeReturn # Details about the outcome of the operation
function gar.decreaseDelegateStake(gatewayAddress, delegator, qty, currentTimestamp, messageId, instantWithdraw)
assert(type(qty) == "number", "Quantity is required and must be a number")
assert(qty > 0, "Quantity must be greater than 0")
Expand All @@ -624,9 +641,10 @@ function gar.decreaseDelegateStake(gatewayAddress, delegator, qty, currentTimest
assert(gateway, "Gateway not found")
assert(gateway.status ~= "leaving", "Gateway is leaving the network and cannot withdraw more stake.")

assert(gateway.delegates[delegator], "This delegate is not staked at this gateway.")
local delegate = gateway.delegates[delegator]
assert(delegate, "This delegate is not staked at this gateway.")

local existingStake = gateway.delegates[delegator].delegatedStake
local existingStake = delegate.delegatedStake
local requiredMinimumStake = gateway.settings.minDelegatedStake
local maxAllowedToWithdraw = existingStake - requiredMinimumStake
assert(
Expand All @@ -645,12 +663,14 @@ function gar.decreaseDelegateStake(gatewayAddress, delegator, qty, currentTimest
else
createDelegateWithdrawVault(gateway, delegator, messageId, qty, currentTimestamp)
end
decreaseDelegateStakeAtGateway(delegator, gateway, qty)
local updatedDelegate, pruned = decreaseDelegateStakeAtGateway(delegator, gateway, qty)

-- update the gateway
GatewayRegistry[gatewayAddress] = gateway
return {
gateway = gateway,
gatewayTotalDelegatedStake = gateway.totalDelegatedStake,
updatedDelegate = updatedDelegate,
delegatePruned = pruned,
penaltyRate = penaltyRate,
expeditedWithdrawalFee = expeditedWithdrawalFee,
amountWithdrawn = amountToWithdraw,
Expand Down Expand Up @@ -1219,16 +1239,20 @@ end
--- Preserves delegate's position in allow list upon removal from gateway
--- @param delegateAddress string The address of the delegator
--- @param gateway table The gateway from which the delegate is being removed
--- @return boolean # Whether or not the delegate was pruned
function gar.pruneDelegateFromGatewayIfNecessary(delegateAddress, gateway)
local pruned = false
local delegate = gateway.delegates[delegateAddress]
if delegate.delegatedStake == 0 and utils.lengthOfTable(delegate.vaults) == 0 then
gateway.delegates[delegateAddress] = nil
pruned = true

-- replace the delegate in the allowedDelegatesLookup table if necessary
if gateway.settings.allowedDelegatesLookup then
gateway.settings.allowedDelegatesLookup[delegateAddress] = true
end
end
return pruned
end

--- Add delegate addresses to the allowedDelegatesLookup table in the gateway's settings
Expand Down
Loading

0 comments on commit 029a449

Please sign in to comment.